diff --git a/app/Makefile b/app/Makefile index fba1326d1..d662c1a4d 100644 --- a/app/Makefile +++ b/app/Makefile @@ -1,6 +1,6 @@ app_objs += $(addprefix app/,\ app.o\ - text_input.o) + utils.o) products += $(app_objs) app.elf app.hex app.bin app.elf: $(app_objs) diff --git a/app/app.cpp b/app/app.cpp index 043984995..eede4f5e7 100644 --- a/app/app.cpp +++ b/app/app.cpp @@ -6,7 +6,7 @@ extern "C" { #include -#include "text_input.h" +#include "utils.h" void draw_lines_from_center() { KDCoordinate width = SCREEN_WIDTH; @@ -60,16 +60,6 @@ void funnyPlot() { delete e; } -static void clear_screen() { - KDRect r; - r.x = 0; - r.y = 0; - r.width = SCREEN_WIDTH; - r.height = SCREEN_HEIGHT; - KDFillRect(r, 0x00); -} - - static void interactive_expression_parsing() { while (1) { char * text_input = get_text(); diff --git a/app/text_input.cpp b/app/utils.cpp similarity index 92% rename from app/text_input.cpp rename to app/utils.cpp index 522203310..f0e1c16d4 100644 --- a/app/text_input.cpp +++ b/app/utils.cpp @@ -7,6 +7,15 @@ extern "C" { #define PROMPT_HEIGHT 30 +void clear_screen() { + KDRect r; + r.x = 0; + r.y = 0; + r.width = SCREEN_WIDTH; + r.height = SCREEN_HEIGHT; + KDFillRect(r, 0x00); +} + static void clear_prompt() { KDRect r; r.x = 0; diff --git a/app/text_input.h b/app/utils.h similarity index 61% rename from app/text_input.h rename to app/utils.h index b7490c2ff..ca78fcae1 100644 --- a/app/text_input.h +++ b/app/utils.h @@ -1,8 +1,10 @@ -#ifndef APP_TEXTINPUT_H -#define APP_TEXTINPUT_H +#ifndef APP_UTILS_H +#define APP_UTILS_H /* Returns a pointer to an input text, allocated by the functions (it is thus * the caller's role to free it). */ char* get_text(); -#endif // APP_TEXTINPUT_H +void clear_screen(); + +#endif // APP_UTILS_H diff --git a/poincare/Makefile b/poincare/Makefile index a3d430325..2d7202235 100644 --- a/poincare/Makefile +++ b/poincare/Makefile @@ -4,23 +4,28 @@ objs += $(addprefix poincare/src/,\ binary_operation.o\ commutative_operation.o\ context.o\ + cosine.o\ expression.o\ expression_lexer.o\ expression_parser.o\ float.o\ fraction.o\ + function.o\ integer.o\ leaf_expression.o\ power.o\ product.o\ + sine.o\ subtraction.o\ symbol.o\ + tangent.o\ ) objs += $(addprefix poincare/src/layout/,\ + exponent_layout.o\ expression_layout.o\ fraction_layout.o\ + function_layout.o\ horizontal_layout.o\ - exponent_layout.o\ string_layout.o\ ) objs += $(addprefix poincare/src/simplify/,\ @@ -35,6 +40,7 @@ tests += $(addprefix poincare/test/,\ product.cpp\ simplify.cpp\ simplify_addition_integer.cpp\ + trigo.cpp\ subtraction.cpp\ ) diff --git a/poincare/include/poincare.h b/poincare/include/poincare.h index eeaf014ef..21b774e7c 100644 --- a/poincare/include/poincare.h +++ b/poincare/include/poincare.h @@ -3,13 +3,17 @@ #include #include +#include #include -#include #include +#include +#include #include #include #include +#include #include #include +#include #endif diff --git a/poincare/include/poincare/cosine.h b/poincare/include/poincare/cosine.h new file mode 100644 index 000000000..31f376375 --- /dev/null +++ b/poincare/include/poincare/cosine.h @@ -0,0 +1,14 @@ +#ifndef POINCARE_COSINE_H +#define POINCARE_COSINE_H + +#include + +class Cosine : public Function { + public: + Cosine(Expression * arg, bool clone_arg=true): Function(arg, (char*) "cos", clone_arg) {} + float approximate(Context& context) override; + Type type() override; + Expression * clone() override; +}; + +#endif diff --git a/poincare/include/poincare/expression.h b/poincare/include/poincare/expression.h index 3fe1bbf8f..cedb2ec46 100644 --- a/poincare/include/poincare/expression.h +++ b/poincare/include/poincare/expression.h @@ -10,13 +10,16 @@ class Expression { public: enum class Type : uint8_t { Addition, + Cosine, Float, Fraction, Integer, Power, Product, + Sine, Subtraction, - Symbol + Symbol, + Tangent, }; static Expression * parse(char const * string); virtual ~Expression(); diff --git a/poincare/include/poincare/function.h b/poincare/include/poincare/function.h new file mode 100644 index 000000000..e02e7e388 --- /dev/null +++ b/poincare/include/poincare/function.h @@ -0,0 +1,19 @@ +#ifndef POINCARE_FUNCTION_H +#define POINCARE_FUNCTION_H + +#include + +class Function : public Expression { + public: + Function(Expression * arg, char* function_name, bool clone_operands=true); + ~Function(); + ExpressionLayout * createLayout(ExpressionLayout * parent) override; + Expression * operand(int i) override; + int numberOfOperands() override; + protected: + Expression * m_arg; + private: + char* m_function_name; +}; + +#endif diff --git a/poincare/include/poincare/sine.h b/poincare/include/poincare/sine.h new file mode 100644 index 000000000..fe53563a0 --- /dev/null +++ b/poincare/include/poincare/sine.h @@ -0,0 +1,14 @@ +#ifndef POINCARE_SINE_H +#define POINCARE_SINE_H + +#include + +class Sine : public Function { + public: + Sine(Expression * arg, bool clone_arg=true): Function(arg, (char*) "sin", clone_arg) {} + float approximate(Context& context) override; + Type type() override; + Expression * clone() override; +}; + +#endif diff --git a/poincare/include/poincare/tangent.h b/poincare/include/poincare/tangent.h new file mode 100644 index 000000000..06ffbae06 --- /dev/null +++ b/poincare/include/poincare/tangent.h @@ -0,0 +1,14 @@ +#ifndef POINCARE_TANGENT_H +#define POINCARE_TANGENT_H + +#include + +class Tangent : public Function { + public: + Tangent(Expression * arg, bool clone_arg=true): Function(arg, (char*) "tan", clone_arg) {} + float approximate(Context& context) override; + Type type() override; + Expression * clone() override; +}; + +#endif diff --git a/poincare/src/addition.cpp b/poincare/src/addition.cpp index cb4080b33..15795ee7b 100644 --- a/poincare/src/addition.cpp +++ b/poincare/src/addition.cpp @@ -14,6 +14,5 @@ float Addition::operateApproximatevelyOn(float a, float b) { } ExpressionLayout * Addition::createLayout(ExpressionLayout * parent) { - //FIXME: There can be more than two operands now! :-) - return new HorizontalLayout(parent, operand(0), '+', operand(1)); + return new HorizontalLayout(parent, m_operands, m_numberOfOperands, '+'); } diff --git a/poincare/src/commutative_operation.cpp b/poincare/src/commutative_operation.cpp index 929908076..37252e0a0 100644 --- a/poincare/src/commutative_operation.cpp +++ b/poincare/src/commutative_operation.cpp @@ -30,6 +30,7 @@ int CommutativeOperation::numberOfOperands() { } Expression * CommutativeOperation::operand(int i) { + assert(i < m_numberOfOperands); return m_operands[i]; } diff --git a/poincare/src/cosine.cpp b/poincare/src/cosine.cpp new file mode 100644 index 000000000..8fafad923 --- /dev/null +++ b/poincare/src/cosine.cpp @@ -0,0 +1,15 @@ +#include +#include "layout/horizontal_layout.h" + +Expression * Cosine::clone() { + return new Cosine(m_arg, true); +} + +Expression::Type Cosine::type() { + return Expression::Type::Cosine; +} + +float Cosine::approximate(Context& context) { + // FIXME: use cosine obviously. + return m_arg->approximate(context); +} diff --git a/poincare/src/expression_lexer.l b/poincare/src/expression_lexer.l index 5dd2bb7b4..26a2dffdc 100644 --- a/poincare/src/expression_lexer.l +++ b/poincare/src/expression_lexer.l @@ -42,6 +42,9 @@ class Expression; %% [0-9]+ { yylval->string = yytext; return(INTEGER); } +sin {return(SINE);} +cos {return(COSINE);} +tan {return(TANGENT);} [A-Za-z]+ { yylval->string = yytext; return(SYMBOL); } \+ { return(PLUS); } \- { return(MINUS); } diff --git a/poincare/src/expression_parser.y b/poincare/src/expression_parser.y index 505f1505b..43c501e64 100644 --- a/poincare/src/expression_parser.y +++ b/poincare/src/expression_parser.y @@ -54,6 +54,9 @@ void poincare_expression_yyerror(void * scanner, Expression ** expressionOutput, %token MULTIPLY %token DIVIDE %token POW +%token SINE +%token COSINE +%token TANGENT %token LEFT_PARENTHESIS %token RIGHT_PARENTHESIS @@ -85,6 +88,9 @@ exp: | exp DIVIDE exp { Expression * terms[2] = {$1,$3}; $$ = new Fraction(terms, false); } | exp POW exp { Expression * terms[2] = {$1,$3}; $$ = new Power(terms, false); } | LEFT_PARENTHESIS exp RIGHT_PARENTHESIS { $$ = $2; } + | SINE LEFT_PARENTHESIS exp RIGHT_PARENTHESIS { $$ = new Sine($3); } + | COSINE LEFT_PARENTHESIS exp RIGHT_PARENTHESIS { $$ = new Cosine($3); } + | TANGENT LEFT_PARENTHESIS exp RIGHT_PARENTHESIS { $$ = new Tangent($3); } ; %% diff --git a/poincare/src/function.cpp b/poincare/src/function.cpp new file mode 100644 index 000000000..e0ac86286 --- /dev/null +++ b/poincare/src/function.cpp @@ -0,0 +1,35 @@ +extern "C" { +#include +} +#include +#include "layout/function_layout.h" + +Function::Function(Expression * arg, char* function_name, bool clone_operands) { + m_arg = (Expression *)malloc(sizeof(Expression)); + m_function_name = function_name; + if (clone_operands) { + m_arg = arg->clone(); + } else { + m_arg = arg; + } +} + +Function::~Function() { + delete m_arg; +} + +ExpressionLayout * Function::createLayout(ExpressionLayout * parent) { + return new FunctionLayout(parent, m_function_name, m_arg); +} + +Expression * Function::operand(int i) { + if (i==0) { + return m_arg; + } else { + return nullptr; + } +} + +int Function::numberOfOperands() { + return 1; +} diff --git a/poincare/src/layout/function_layout.cpp b/poincare/src/layout/function_layout.cpp new file mode 100644 index 000000000..02bc5410b --- /dev/null +++ b/poincare/src/layout/function_layout.cpp @@ -0,0 +1,73 @@ +extern "C" { +#include +#include +} +#include "function_layout.h" +#include "string_layout.h" + +// This code seems like a duplicate of the horizontal layout but it isn't, indeed the horizontal +// layout is used to print the same operation applied to different expressions such that: +// "expr_1 + epr_2 + expr_3 + expr_4". +// Here we want the pattern +// FUNCTION_NAME(expr). +// Thus the code in horizontal layer is not really reusable. + +FunctionLayout::FunctionLayout(ExpressionLayout * parent, char* function_name, Expression * argument) : +ExpressionLayout(parent) { + m_children[0] = new StringLayout(this, function_name, 1); + + char string[2] = {'(', '\0'}; + m_children[1] = new StringLayout(this, string, 1); + + m_children[2] = argument->createLayout(this); + + string[0] = ')'; + m_children[3] = new StringLayout(this, string, 1); +} + +FunctionLayout::~FunctionLayout() { + for (int i(0); i<4; i++) { + delete m_children[i]; + } +} + +void FunctionLayout::render(KDPoint point) { } + +KDSize FunctionLayout::computeSize() { + KDSize size = (KDSize){.width = 0, .height = 0}; + int i = 0; + while (ExpressionLayout * c = child(i++)) { + KDSize childSize = c->size(); + size.width += childSize.width; + if (childSize.height > size.height) { + size.height = childSize.height; + } + } + return size; +} + +ExpressionLayout * FunctionLayout::child(uint16_t index) { + if (index >= 4) { + return nullptr; + } + return m_children[index]; +} + +KDPoint FunctionLayout::positionOfChild(ExpressionLayout * child) { + KDPoint position = (KDPoint){.x = 0, .y = 0}; + uint16_t index = 0; + for (int i=0;i<4;i++) { + if (m_children[i] == child) { + index = i; + break; + } + } + if (index > 0) { + ExpressionLayout * previousChild = m_children[index-1]; + assert(previousChild != nullptr); + position.x = previousChild->origin().x + previousChild->size().width; + } + position.y = (size().height - child->size().height)/2; + return position; +} + diff --git a/poincare/src/layout/function_layout.h b/poincare/src/layout/function_layout.h new file mode 100644 index 000000000..0b689dfcf --- /dev/null +++ b/poincare/src/layout/function_layout.h @@ -0,0 +1,21 @@ +#ifndef POINCARE_FUNCTION_LAYOUT_H +#define POINCARE_FUNCTION_LAYOUT_H + +#include +#include + +class FunctionLayout : public ExpressionLayout { + public: + FunctionLayout(ExpressionLayout * parent, char* function_name, Expression * arg); + ~FunctionLayout(); + protected: + void render(KDPoint point) override; + KDSize computeSize() override; + ExpressionLayout * child(uint16_t index) override; + KDPoint positionOfChild(ExpressionLayout * child) override; + private: + ExpressionLayout * m_children[4]; +}; + +#endif + diff --git a/poincare/src/layout/horizontal_layout.cpp b/poincare/src/layout/horizontal_layout.cpp index e1297a0d2..eebc343b0 100644 --- a/poincare/src/layout/horizontal_layout.cpp +++ b/poincare/src/layout/horizontal_layout.cpp @@ -1,29 +1,31 @@ extern "C" { -#include #include +#include +#include } #include "horizontal_layout.h" #include "string_layout.h" -HorizontalLayout::HorizontalLayout(ExpressionLayout * parent, Expression * left_expression, char symbol, Expression * right_expression) : -ExpressionLayout(parent ) { - m_children[0] = left_expression->createLayout(this); - +HorizontalLayout::HorizontalLayout(ExpressionLayout * parent, Expression ** operands,int number_of_operands, char symbol) : ExpressionLayout(parent) { + assert(number_of_operands > 0); + m_number_of_children = 2*number_of_operands-1; + m_children_layouts = (ExpressionLayout **)malloc(m_number_of_children*sizeof(ExpressionLayout *)); char string[2] = {symbol, '\0'}; - m_children[1] = new StringLayout(this, string, 1); - - m_children[2] = right_expression->createLayout(this); + m_children_layouts[0] = operands[0]->createLayout(this); + for (int i=1; icreateLayout(this); + } } HorizontalLayout::~HorizontalLayout() { - delete m_children[2]; - delete m_children[1]; - delete m_children[0]; + for (int i=0; i= 3) { + assert(index <= m_number_of_children); + if (index < m_number_of_children) { + return m_children_layouts[index]; + } else { return nullptr; } - return m_children[index]; } KDPoint HorizontalLayout::positionOfChild(ExpressionLayout * child) { KDPoint position = (KDPoint){.x = 0, .y = 0}; uint16_t index = 0; - for (int i=0;i<3;i++) { - if (m_children[i] == child) { + for (int i=0;i 0) { - ExpressionLayout * previousChild = m_children[index-1]; + ExpressionLayout * previousChild = m_children_layouts[index-1]; assert(previousChild != nullptr); position.x = previousChild->origin().x + previousChild->size().width; } diff --git a/poincare/src/layout/horizontal_layout.h b/poincare/src/layout/horizontal_layout.h index d4cb6a04a..0ee133f5c 100644 --- a/poincare/src/layout/horizontal_layout.h +++ b/poincare/src/layout/horizontal_layout.h @@ -6,7 +6,7 @@ class HorizontalLayout : public ExpressionLayout { public: - HorizontalLayout(ExpressionLayout * parent, Expression * left_expression, char symbol, Expression * right_expression); + HorizontalLayout(ExpressionLayout * parent, Expression ** operands, int number_of_children, char symbol); ~HorizontalLayout(); protected: void render(KDPoint point) override; @@ -14,7 +14,8 @@ class HorizontalLayout : public ExpressionLayout { ExpressionLayout * child(uint16_t index) override; KDPoint positionOfChild(ExpressionLayout * child) override; private: - ExpressionLayout * m_children[3]; + int m_number_of_children; + ExpressionLayout ** m_children_layouts; }; #endif diff --git a/poincare/src/product.cpp b/poincare/src/product.cpp index fc888bfd8..36c93dcba 100644 --- a/poincare/src/product.cpp +++ b/poincare/src/product.cpp @@ -17,6 +17,5 @@ Expression::Type Product::type() { } ExpressionLayout * Product::createLayout(ExpressionLayout * parent) { - //FIXME: There can be more than two factors now! :-) - return new HorizontalLayout(parent, operand(0), '*', operand(1)); + return new HorizontalLayout(parent, m_operands, m_numberOfOperands, '*'); } diff --git a/poincare/src/sine.cpp b/poincare/src/sine.cpp new file mode 100644 index 000000000..fb3d7003c --- /dev/null +++ b/poincare/src/sine.cpp @@ -0,0 +1,15 @@ +#include +#include "layout/horizontal_layout.h" + +Expression * Sine::clone() { + return new Sine(m_arg, true); +} + +Expression::Type Sine::type() { + return Expression::Type::Sine; +} + +float Sine::approximate(Context& context) { + // FIXME: use sine obviously. + return m_arg->approximate(context); +} diff --git a/poincare/src/subtraction.cpp b/poincare/src/subtraction.cpp index 7f82f02db..448553f2a 100644 --- a/poincare/src/subtraction.cpp +++ b/poincare/src/subtraction.cpp @@ -14,5 +14,5 @@ Expression::Type Subtraction::type() { } ExpressionLayout * Subtraction::createLayout(ExpressionLayout * parent) { - return new HorizontalLayout(parent, m_operands[0], '-', m_operands[1]); + return new HorizontalLayout(parent, m_operands, 2, '-'); } diff --git a/poincare/src/tangent.cpp b/poincare/src/tangent.cpp new file mode 100644 index 000000000..6eb92af4c --- /dev/null +++ b/poincare/src/tangent.cpp @@ -0,0 +1,15 @@ +#include +#include "layout/horizontal_layout.h" + +Expression * Tangent::clone() { + return new Tangent(m_arg, true); +} + +Expression::Type Tangent::type() { + return Expression::Type::Tangent; +} + +float Tangent::approximate(Context& context) { + // FIXME: use tangent obviously. + return m_arg->approximate(context); +} diff --git a/poincare/test/trigo.cpp b/poincare/test/trigo.cpp new file mode 100644 index 000000000..700ac5b64 --- /dev/null +++ b/poincare/test/trigo.cpp @@ -0,0 +1,21 @@ +#include +#include +#include + +QUIZ_CASE(poincare_parse_trigo) { + { + Expression * e = Expression::parse("sin(0)"); + Expression * e2 = e->simplify(); + assert(e2->type() == Expression::Type::Sine); + } + { + Expression * e = Expression::parse("cos(0)"); + Expression * e2 = e->simplify(); + assert(e2->type() == Expression::Type::Cosine); + } + { + Expression * e = Expression::parse("tan(0)"); + Expression * e2 = e->simplify(); + assert(e2->type() == Expression::Type::Tangent); + } +} diff --git a/quiz/src/runner.c b/quiz/src/runner.c index a19e202d8..bb76367cb 100644 --- a/quiz/src/runner.c +++ b/quiz/src/runner.c @@ -10,7 +10,6 @@ void print(char * message) { line_y += line_height; if (line_y > SCREEN_HEIGHT) { line_y = 0; - ion_getchar(); // Clear screen maybe? } }