From 81a33828d805ddd340304108814c458e9a7277c7 Mon Sep 17 00:00:00 2001 From: Stephane Del Pino <stephane.delpino44@gmail.com> Date: Wed, 18 Sep 2019 18:52:55 +0200 Subject: [PATCH] Begin functions (mathematical applications) implementation The idea is to define functions this way `` let f : X -> Y, x -> y; `` where y is an expression of x and previously defined variables. - x is local to the function! - y cannot change *any* of its parameters (neither x or previously defined data) These will not be C/C++-like functions (which will be referred as routines) In this commit X and Y are basic types such as N, Z, R, B and string are allowed. The aim is to allow constructions like this, for instance: `` // definition let norm : R*R -> R, (x,y) -> sqrt(x*x + y*y); // usage R n = norm(2,3.2); `` Standard functions such as sin, cos, sqrt, abs, ... should be predefined functions of that kind. This commit only defines the simplest grammar. Definition is checked but use is still invalid (since function definition is not stored correctly according with regard to the function variable) --- src/language/ASTBuilder.cpp | 111 +++++++++--------- src/language/ASTNodeDataType.cpp | 3 + src/language/ASTNodeDataType.hpp | 1 + src/language/ASTNodeDataTypeBuilder.cpp | 42 +++++++ src/language/ASTNodeExpressionBuilder.cpp | 5 + .../ASTSymbolInitializationChecker.cpp | 14 +++ src/language/ASTSymbolTableBuilder.cpp | 16 +++ src/language/PEGGrammar.hpp | 33 ++++-- src/language/PugsParser.cpp | 2 +- 9 files changed, 165 insertions(+), 62 deletions(-) diff --git a/src/language/ASTBuilder.cpp b/src/language/ASTBuilder.cpp index e8c81778f..984ed5cba 100644 --- a/src/language/ASTBuilder.cpp +++ b/src/language/ASTBuilder.cpp @@ -201,60 +201,63 @@ struct ASTBuilder::simplify_stream_statement : parse_tree::apply<ASTBuilder::sim }; template <typename Rule> -using selector = - parse_tree::selector<Rule, - parse_tree::store_content::on<true_kw, - false_kw, - integer, - real, - literal, - name, - B_set, - N_set, - Z_set, - R_set, - string_type, - cout_kw, - cerr_kw, - clog_kw, - declaration, - if_statement, - do_while_statement, - while_statement, - for_statement, - break_kw, - continue_kw>, - ASTBuilder::rearrange::on<product, affectation, expression>, - ASTBuilder::simplify_unary::on<unary_minus, unary_plus, unary_not, unary_expression>, - parse_tree::remove_content::on<plus_op, - minus_op, - multiply_op, - divide_op, - lesser_op, - lesser_or_eq_op, - greater_op, - greater_or_eq_op, - eqeq_op, - not_eq_op, - and_op, - or_op, - xor_op, - eq_op, - multiplyeq_op, - divideeq_op, - pluseq_op, - minuseq_op, - unary_plusplus, - unary_minusminus, - post_minusminus, - post_plusplus>, - ASTBuilder::simplify_for_statement_block::on<for_statement_block>, - parse_tree::discard_empty::on<ignored, semicol, block>, - ASTBuilder::simplify_statement_block::on<statement_block>, - ASTBuilder::simplify_for_init::on<for_init>, - ASTBuilder::simplify_for_test::on<for_test>, - ASTBuilder::simplify_for_post::on<for_post>, - ASTBuilder::simplify_stream_statement::on<ostream_statement>>; +using selector = parse_tree::selector< + Rule, + parse_tree::store_content::on<true_kw, + false_kw, + integer, + real, + literal, + name, + B_set, + N_set, + Z_set, + R_set, + string_type, + cout_kw, + cerr_kw, + clog_kw, + declaration, + let_declaration, + function_domain_mapping, + function_definition, + if_statement, + do_while_statement, + while_statement, + for_statement, + break_kw, + continue_kw>, + ASTBuilder::rearrange::on<product, affectation, expression>, + ASTBuilder::simplify_unary::on<unary_minus, unary_plus, unary_not, function_evaluation, unary_expression>, + parse_tree::remove_content::on<plus_op, + minus_op, + multiply_op, + divide_op, + lesser_op, + lesser_or_eq_op, + greater_op, + greater_or_eq_op, + eqeq_op, + not_eq_op, + and_op, + or_op, + xor_op, + eq_op, + multiplyeq_op, + divideeq_op, + pluseq_op, + minuseq_op, + unary_plusplus, + unary_minusminus, + post_minusminus, + post_plusplus>, + ASTBuilder::simplify_for_statement_block::on<for_statement_block>, + parse_tree::discard_empty::on<ignored, semicol, block>, + ASTBuilder::simplify_statement_block::on<statement_block>, + ASTBuilder::simplify_for_init::on<for_init>, + ASTBuilder::simplify_for_test::on<for_test>, + ASTBuilder::simplify_for_post::on<for_post>, + ASTBuilder::simplify_stream_statement::on<ostream_statement>>; template <typename InputT> std::unique_ptr<ASTNode> diff --git a/src/language/ASTNodeDataType.cpp b/src/language/ASTNodeDataType.cpp index 855a3e04a..1220df563 100644 --- a/src/language/ASTNodeDataType.cpp +++ b/src/language/ASTNodeDataType.cpp @@ -26,6 +26,9 @@ dataTypeName(const ASTNodeDataType& data_type) case ASTNodeDataType::typename_t: name = "typename"; break; + case ASTNodeDataType::function_t: + name = "function"; + break; case ASTNodeDataType::void_t: name = "void"; break; diff --git a/src/language/ASTNodeDataType.hpp b/src/language/ASTNodeDataType.hpp index d6153c101..daabf0c0e 100644 --- a/src/language/ASTNodeDataType.hpp +++ b/src/language/ASTNodeDataType.hpp @@ -13,6 +13,7 @@ enum class ASTNodeDataType : int32_t double_t = 3, string_t = 5, typename_t = 10, + function_t = 11, void_t = std::numeric_limits<int32_t>::max() }; diff --git a/src/language/ASTNodeDataTypeBuilder.cpp b/src/language/ASTNodeDataTypeBuilder.cpp index 020e7baed..cfdc0187c 100644 --- a/src/language/ASTNodeDataTypeBuilder.cpp +++ b/src/language/ASTNodeDataTypeBuilder.cpp @@ -51,6 +51,46 @@ ASTNodeDataTypeBuilder::_buildNodeDataTypes(ASTNode& n) Assert(found); i_symbol->second.setDataType(data_type); n.m_data_type = data_type; + } else if (n.is<language::let_declaration>()) { + n.children[0]->m_data_type = ASTNodeDataType::function_t; + + n.children[1]->children[0]->m_data_type = ASTNodeDataType::typename_t; + n.children[1]->children[1]->m_data_type = ASTNodeDataType::typename_t; + + { // Function data type + const std::string& symbol = n.children[0]->string(); + + std::shared_ptr<SymbolTable>& symbol_table = n.m_symbol_table; + + auto [i_symbol, found] = symbol_table->find(symbol, n.children[0]->begin()); + Assert(found); + i_symbol->second.setDataType(n.children[0]->m_data_type); + } + auto& type_node = *(n.children[1]->children[0]); + ASTNodeDataType data_type{ASTNodeDataType::undefined_t}; + if (type_node.is<language::B_set>()) { + data_type = ASTNodeDataType::bool_t; + } else if (type_node.is<language::Z_set>()) { + data_type = ASTNodeDataType::int_t; + } else if (type_node.is<language::N_set>()) { + data_type = ASTNodeDataType::unsigned_int_t; + } else if (type_node.is<language::R_set>()) { + data_type = ASTNodeDataType::double_t; + } else if (type_node.is<language::string_type>()) { + data_type = ASTNodeDataType::string_t; + } + + Assert(data_type != ASTNodeDataType::undefined_t); // LCOV_EXCL_LINE + + n.children[2]->children[0]->m_data_type = data_type; + const std::string& symbol = n.children[2]->children[0]->string(); + + std::shared_ptr<SymbolTable>& symbol_table = n.m_symbol_table; + + auto [i_symbol, found] = symbol_table->find(symbol, n.children[2]->children[0]->begin()); + Assert(found); + i_symbol->second.setDataType(data_type); + n.m_data_type = ASTNodeDataType::void_t; } else if (n.is<language::name>()) { std::shared_ptr<SymbolTable>& symbol_table = n.m_symbol_table; @@ -68,6 +108,8 @@ ASTNodeDataTypeBuilder::_buildNodeDataTypes(ASTNode& n) } else if (n.is<language::eq_op>() or n.is<language::multiplyeq_op>() or n.is<language::divideeq_op>() or n.is<language::pluseq_op>() or n.is<language::minuseq_op>()) { n.m_data_type = n.children[0]->m_data_type; + } else if (n.is<language::function_domain_mapping>() or n.is<language::function_definition>()) { + n.m_data_type = ASTNodeDataType::void_t; } else if (n.is<language::for_post>() or n.is<language::for_init>() or n.is<language::for_statement_block>()) { n.m_data_type = ASTNodeDataType::void_t; } else if (n.is<language::for_test>()) { diff --git a/src/language/ASTNodeExpressionBuilder.cpp b/src/language/ASTNodeExpressionBuilder.cpp index e6dd686c6..f7157005e 100644 --- a/src/language/ASTNodeExpressionBuilder.cpp +++ b/src/language/ASTNodeExpressionBuilder.cpp @@ -27,6 +27,11 @@ ASTNodeExpressionBuilder::_buildExpression(ASTNode& n) n.is<language::pluseq_op>() or n.is<language::minuseq_op>())) { ASTNodeAffectationExpressionBuilder{n}; + } else if (n.is<language::let_declaration>()) { + std::cerr << rang::fgB::red << "\"Let expression\" is not defined correctly" << rang::style::reset << '\n'; + n.m_node_processor = std::make_unique<FakeProcessor>(); + return; + } else if (n.is<language::real>()) { n.m_node_processor = std::make_unique<FakeProcessor>(); } else if (n.is<language::integer>()) { diff --git a/src/language/ASTSymbolInitializationChecker.cpp b/src/language/ASTSymbolInitializationChecker.cpp index 06b0e9975..1c75f86a4 100644 --- a/src/language/ASTSymbolInitializationChecker.cpp +++ b/src/language/ASTSymbolInitializationChecker.cpp @@ -15,6 +15,20 @@ ASTSymbolInitializationChecker::_checkSymbolInitialization(ASTNode& node) this->_checkSymbolInitialization(*node.children[2]); i_symbol->second.setIsInitialized(); } + } else if (node.is<language::let_declaration>()) { + const std::string& symbol = node.children[0]->string(); + auto [i_symbol, found] = node.m_symbol_table->find(symbol, node.children[0]->begin()); + Assert(found, "unexpected error, should have been detected through declaration checking"); + if (node.children.size() == 3) { + this->_checkSymbolInitialization(*node.children[2]); + i_symbol->second.setIsInitialized(); + } + } else if (node.is<language::function_definition>()) { + const std::string& symbol = node.children[0]->string(); + auto [i_symbol, found] = node.m_symbol_table->find(symbol, node.children[0]->begin()); + Assert(found, "unexpected error, should have been detected through declaration checking"); + i_symbol->second.setIsInitialized(); + this->_checkSymbolInitialization(*node.children[1]); } else if (node.is<language::eq_op>()) { // first checks for right hand side this->_checkSymbolInitialization(*node.children[1]); diff --git a/src/language/ASTSymbolTableBuilder.cpp b/src/language/ASTSymbolTableBuilder.cpp index 5fadaf4e0..0d3c08232 100644 --- a/src/language/ASTSymbolTableBuilder.cpp +++ b/src/language/ASTSymbolTableBuilder.cpp @@ -25,6 +25,22 @@ ASTSymbolTableBuilder::buildSymbolTable(ASTNode& n, std::shared_ptr<SymbolTable> error_message << "symbol '" << rang::fg::red << symbol << rang::fg::reset << '\'' << " was already defined!"; throw parse_error(error_message.str(), std::vector{n.begin()}); } + } else if (n.is<language::let_declaration>()) { + const std::string& symbol = n.children[0]->string(); + auto [i_symbol, success] = symbol_table->add(symbol, n.children[0]->begin()); + if (not success) { + std::ostringstream error_message; + error_message << "symbol '" << rang::fg::red << symbol << rang::fg::reset << '\'' << " was already defined!"; + throw parse_error(error_message.str(), std::vector{n.begin()}); + } + } else if (n.is<language::function_definition>()) { + const std::string& symbol = n.children[0]->string(); + auto [i_symbol, success] = symbol_table->add(symbol, n.children[0]->begin()); + if (not success) { + std::ostringstream error_message; + error_message << "symbol '" << rang::fg::red << symbol << rang::fg::reset << '\'' << " was already defined!"; + throw parse_error(error_message.str(), std::vector{n.begin()}); + } } else if (n.is<language::name>()) { auto [i_symbol, found] = symbol_table->find(n.string(), n.begin()); if (not found) { diff --git a/src/language/PEGGrammar.hpp b/src/language/PEGGrammar.hpp index 0c308405d..3a8eda150 100644 --- a/src/language/PEGGrammar.hpp +++ b/src/language/PEGGrammar.hpp @@ -56,8 +56,6 @@ struct literal : if_must< one< '"' >, until< one< '"' >, character > > {}; struct LITERAL : seq< literal, ignored >{}; -struct semicol : one< ';' >{}; - struct REAL : seq< real, ignored >{}; struct B_set : one< 'B' >{}; @@ -82,6 +80,9 @@ struct false_kw : TAO_PEGTL_KEYWORD("false") {}; struct BOOL : seq< sor<true_kw, false_kw>, ignored > {}; +struct let_kw : TAO_PEGTL_KEYWORD("let") {}; +struct LET : seq < let_kw, ignored > {}; + struct do_kw : TAO_PEGTL_KEYWORD("do") {}; struct DO : seq < do_kw, ignored > {}; @@ -109,18 +110,30 @@ struct clog_kw : TAO_PEGTL_KEYWORD("clog") {}; struct keywork : sor < basic_type, true_kw, false_kw, do_kw, while_kw, for_kw, if_kw, else_kw, and_kw, or_kw, xor_kw, break_kw, continue_kw, cout_kw, cerr_kw, clog_kw > {}; -struct name : minus< identifier, keywork > {}; +struct name : minus< identifier, keywork > {}; struct NAME : seq < name, ignored > {}; +struct right_arrow_kw : seq< one< '-' >, one< '>' > > {}; +struct RIGHT_ARROW : seq< right_arrow_kw, ignored > {}; + +struct semicol : one< ';' > {}; struct SEMICOL : seq< semicol , ignored > {}; +struct column : one< ':' > {}; +struct COLUMN : seq< column , ignored > {}; + +struct comma : one< ',' > {}; +struct COMMA : seq< comma , ignored > {}; + struct open_parent : seq< one< '(' >, ignored > {}; struct close_parent : seq< one< ')' >, ignored > {}; struct expression; struct parented_expression : if_must< open_parent, expression, close_parent > {}; -struct primary_expression : sor< BOOL, REAL, INTEGER, LITERAL, NAME, parented_expression > {}; +struct function_evaluation : seq< NAME, parented_expression > {}; + +struct primary_expression : sor< BOOL, REAL, INTEGER, LITERAL, function_evaluation, NAME, parented_expression > {}; struct unary_plusplus : TAO_PEGTL_STRING("++") {}; struct unary_minusminus : TAO_PEGTL_STRING("--") {}; @@ -188,7 +201,12 @@ struct affect_op : sor< eq_op, multiplyeq_op, divideeq_op, pluseq_op, minuseq_op struct affectation : seq< NAME , if_must< affect_op, expression > >{}; -struct declaration : if_must<TYPESPECIFIER, NAME, opt<if_must<seq<one<'='>,ignored>, expression>> >{}; +struct declaration : if_must< TYPESPECIFIER, NAME, opt< if_must< seq< one< '=' >, ignored >, expression > > >{}; + +struct function_domain_mapping : seq< TYPESPECIFIER, RIGHT_ARROW, TYPESPECIFIER >{}; +struct function_definition : seq< NAME, RIGHT_ARROW, expression >{}; + +struct let_declaration : if_must< LET, NAME, COLUMN, function_domain_mapping, COMMA, function_definition >{}; struct open_brace : seq< one< '{' >, ignored >{}; struct close_brace : seq< one< '}' >, ignored >{}; @@ -198,7 +216,7 @@ struct instruction_list; struct braced_instruction_list : sor<try_catch< open_brace, instruction_list, close_brace >, // non matching braces management - if_must< at< one< '{' > >, raise<open_brace>, until<eof> > >{}; + if_must< at< one< '{' > >, raise< open_brace >, until< eof > > >{}; struct block : braced_instruction_list {}; @@ -222,7 +240,8 @@ struct ostream_object : seq< sor< cout_kw, cerr_kw, clog_kw >, ignored >{}; struct ostream_statement : seq< ostream_object, star< if_must< shift_left_op, expression, ignored > > >{}; struct instruction - : sor<if_must< declaration, semicol >, + : sor<if_must< let_declaration, semicol >, + if_must< declaration, semicol >, if_must< affectation, semicol >, if_statement, if_must<do_while_statement, semicol>, diff --git a/src/language/PugsParser.cpp b/src/language/PugsParser.cpp index 3c5b8ec4c..13c16e223 100644 --- a/src/language/PugsParser.cpp +++ b/src/language/PugsParser.cpp @@ -56,6 +56,7 @@ parser(const std::string& filename) ASTSymbolInitializationChecker{*root_node}; + ASTNodeDataTypeBuilder{*root_node}; { std::string dot_filename{"parse_tree.dot"}; std::ofstream fout(dot_filename); @@ -64,7 +65,6 @@ parser(const std::string& filename) std::cout << " AST dot file: " << dot_filename << '\n'; } - ASTNodeDataTypeBuilder{*root_node}; ASTNodeDataTypeChecker{*root_node}; ASTNodeValueBuilder{*root_node}; -- GitLab