migrated spirit qi grammar to x3 of mupp variable handling.

This commit is contained in:
2025-12-30 11:02:46 +01:00
parent a040f8b576
commit 046ab98144
10 changed files with 179 additions and 252 deletions

View File

@@ -51,7 +51,7 @@ namespace mupp
* handler to pinpoint the exact location of errors in the source text.
*
* The annotation is performed after successful parsing but before semantic
* analysis, using Boost.Phoenix function objects integrated with the parser.
* analysis, integrated with the Spirit X3 parser.
*
* @tparam Iterator the iterator type for traversing the source code (typically std::string::const_iterator)
*/
@@ -59,9 +59,6 @@ namespace mupp
template <typename Iterator>
struct PAnnotation
{
/// Result type specification required by Boost.Phoenix function protocol
template <typename, typename>
struct result { typedef void type; };
std::vector<Iterator>& iters; ///< Reference to vector storing iterator positions indexed by AST node IDs
@@ -79,8 +76,6 @@ namespace mupp
*/
struct set_id
{
typedef void result_type; ///< Return type for Boost.Phoenix compatibility
int id; ///< The ID to assign to tagged AST nodes
/**

View File

@@ -46,7 +46,7 @@ namespace mupp
*
* This error handler logs detailed error information to a file, including
* the error message, line number, source line, and a visual pointer to the
* error position. It integrates with the Boost.Spirit parser error handling
* error position. It integrates with the Boost.Spirit X3 parser error handling
* mechanism and works in conjunction with the PAnnotation handler to map
* AST node IDs back to source positions.
*
@@ -58,9 +58,6 @@ namespace mupp
template <typename Iterator>
struct PErrorHandler
{
/// Result type specification required by Boost.Phoenix function protocol
template <typename, typename, typename>
struct result { typedef void type; };
/**
* @brief Constructor that stores the source code range.

View File

@@ -33,21 +33,12 @@
#ifndef _PEXPRESSION_HPP_
#define _PEXPRESSION_HPP_
///////////////////////////////////////////////////////////////////////////////
// Spirit v2.5 allows you to suppress automatic generation
// of predefined terminals to speed up complation. With
// BOOST_SPIRIT_NO_PREDEFINED_TERMINALS defined, you are
// responsible in creating instances of the terminals that
// you need (e.g. see qi::uint_type uint_ below).
#define BOOST_SPIRIT_NO_PREDEFINED_TERMINALS
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// Uncomment this if you want to enable debugging
// #define BOOST_SPIRIT_QI_DEBUG
// #define BOOST_SPIRIT_X3_DEBUG
///////////////////////////////////////////////////////////////////////////////
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/home/x3.hpp>
#include "PAst.hpp"
#include "PErrorHandler.hpp"
#include "PSkipper.hpp"
@@ -55,14 +46,14 @@
namespace mupp { namespace parser
{
namespace qi = boost::spirit::qi;
namespace ascii = boost::spirit::ascii;
namespace x3 = boost::spirit::x3;
namespace ascii = boost::spirit::x3::ascii;
///////////////////////////////////////////////////////////////////////////////
/**
* @brief The PExpression grammar for parsing mathematical expressions.
*
* This Boost.Spirit grammar defines the syntax for parsing arithmetic
* This Boost.Spirit X3 grammar defines the syntax for parsing arithmetic
* expressions with proper operator precedence and associativity:
* - Primary expressions: numbers, variables, functions, parenthesized expressions
* - Unary operators: +, - (highest precedence)
@@ -80,31 +71,27 @@ namespace mupp { namespace parser
* - 3.14 * $radius
* - sin($theta) + cos($phi)
* - pow($x, 2.0) + pow($y, 2.0)
*
* @tparam Iterator the iterator type for the input (typically std::string::const_iterator)
*/
///////////////////////////////////////////////////////////////////////////////
template <typename Iterator>
struct PExpression : qi::grammar<Iterator, ast::expression(), PSkipper<Iterator> >
{
/**
* @brief Constructor that initializes the grammar rules.
* @param error_handler reference to the error handler for reporting parse errors
*/
PExpression(PErrorHandler<Iterator>& error_handler);
qi::symbols<char, ast::optoken> additive_op; ///< Symbol table for additive operators (+, -)
qi::symbols<char, ast::optoken> multiplicative_op; ///< Symbol table for multiplicative operators (*, /)
qi::symbols<char, ast::optoken> unary_op; ///< Symbol table for unary operators (+, -)
qi::symbols<char, ast::funid> fun_tok; ///< Symbol table for function names
// Rule IDs
struct expr_class;
struct additive_expr_class;
struct multiplicative_expr_class;
struct unary_expr_class;
struct primary_expr_class;
struct identifier_class;
qi::rule<Iterator, ast::expression(), PSkipper<Iterator> > expr; ///< Top-level expression rule
qi::rule<Iterator, ast::expression(), PSkipper<Iterator> > additive_expr; ///< Additive expression rule (lowest precedence)
qi::rule<Iterator, ast::expression(), PSkipper<Iterator> > multiplicative_expr; ///< Multiplicative expression rule (medium precedence)
qi::rule<Iterator, ast::operand(), PSkipper<Iterator> > unary_expr; ///< Unary expression rule
qi::rule<Iterator, ast::operand(), PSkipper<Iterator> > primary_expr; ///< Primary expression rule (highest precedence)
qi::rule<Iterator, std::string(), PSkipper<Iterator> > identifier; ///< Identifier rule (variables prefixed with '$')
};
// Rule declarations
typedef x3::rule<expr_class, ast::expression> expr_type;
typedef x3::rule<additive_expr_class, ast::expression> additive_expr_type;
typedef x3::rule<multiplicative_expr_class, ast::expression> multiplicative_expr_type;
typedef x3::rule<unary_expr_class, ast::operand> unary_expr_type;
typedef x3::rule<primary_expr_class, ast::operand> primary_expr_type;
typedef x3::rule<identifier_class, std::string> identifier_type;
BOOST_SPIRIT_DECLARE(expr_type, additive_expr_type, multiplicative_expr_type,
unary_expr_type, primary_expr_type, identifier_type)
}}
#endif // _PEXPRESSION_HPP_

View File

@@ -9,8 +9,8 @@
see https://github.com/boostorg/spirit
This file contains the implementation (definition) of the PExpression
template grammar. It defines the actual grammar rules and their semantic
actions using Boost.Spirit Qi.
grammar. It defines the actual grammar rules and their semantic
actions using Boost.Spirit X3.
The grammar implements expression parsing with proper operator precedence:
- Primary expressions (literals, variables, functions)
@@ -46,93 +46,116 @@
#include "PExpression.hpp"
#include "PErrorHandler.hpp"
#include "PAnnotation.hpp"
#include <boost/phoenix/function.hpp>
namespace mupp { namespace parser
{
template <typename Iterator>
PExpression<Iterator>::PExpression(PErrorHandler<Iterator>& error_handler)
: PExpression::base_type(expr)
{
qi::_1_type _1;
qi::_2_type _2;
qi::_3_type _3;
qi::_4_type _4;
namespace x3 = boost::spirit::x3;
namespace ascii = boost::spirit::x3::ascii;
qi::char_type char_;
qi::double_type double_;
qi::_val_type _val;
qi::raw_type raw;
qi::lexeme_type lexeme;
qi::alpha_type alpha;
qi::alnum_type alnum;
using qi::on_error;
using qi::on_success;
using qi::fail;
using boost::phoenix::function;
typedef function<mupp::PErrorHandler<Iterator> > error_handler_function;
typedef function<mupp::PAnnotation<Iterator> > annotation_function;
using x3::char_;
using x3::double_;
using x3::raw;
using x3::lexeme;
using ascii::alpha;
using ascii::alnum;
///////////////////////////////////////////////////////////////////////
// Tokens
additive_op.add
("+", ast::op_plus)
("-", ast::op_minus)
;
// Symbol tables for operators and functions
///////////////////////////////////////////////////////////////////////
multiplicative_op.add
("*", ast::op_times)
("/", ast::op_divide)
;
inline struct additive_op_ : x3::symbols<ast::optoken>
{
additive_op_()
{
add
("+", ast::op_plus)
("-", ast::op_minus)
;
}
} additive_op;
unary_op.add
("+", ast::op_positive)
("-", ast::op_negative)
;
inline struct multiplicative_op_ : x3::symbols<ast::optoken>
{
multiplicative_op_()
{
add
("*", ast::op_times)
("/", ast::op_divide)
;
}
} multiplicative_op;
fun_tok.add
("max", ast::fun_max)
("min", ast::fun_min)
("abs", ast::fun_abs)
("sin", ast::fun_sin)
("cos", ast::fun_cos)
("tan", ast::fun_tan)
("sinh", ast::fun_sinh)
("cosh", ast::fun_cosh)
("tanh", ast::fun_tanh)
("asin", ast::fun_asin)
("acos", ast::fun_acos)
("atan", ast::fun_atan)
("exp", ast::fun_exp)
("log", ast::fun_log)
("ln", ast::fun_ln)
("sqrt", ast::fun_sqrt)
;
inline struct unary_op_ : x3::symbols<ast::optoken>
{
unary_op_()
{
add
("+", ast::op_positive)
("-", ast::op_negative)
;
}
} unary_op;
inline struct fun_tok_ : x3::symbols<ast::funid>
{
fun_tok_()
{
add
("max", ast::fun_max)
("min", ast::fun_min)
("abs", ast::fun_abs)
("sin", ast::fun_sin)
("cos", ast::fun_cos)
("tan", ast::fun_tan)
("sinh", ast::fun_sinh)
("cosh", ast::fun_cosh)
("tanh", ast::fun_tanh)
("asin", ast::fun_asin)
("acos", ast::fun_acos)
("atan", ast::fun_atan)
("exp", ast::fun_exp)
("log", ast::fun_log)
("ln", ast::fun_ln)
("sqrt", ast::fun_sqrt)
;
}
} fun_tok;
///////////////////////////////////////////////////////////////////////
// Rule definitions
///////////////////////////////////////////////////////////////////////
inline expr_type const expr = "expr";
inline additive_expr_type const additive_expr = "additive_expr";
inline multiplicative_expr_type const multiplicative_expr = "multiplicative_expr";
inline unary_expr_type const unary_expr = "unary_expr";
inline primary_expr_type const primary_expr = "primary_expr";
inline identifier_type const identifier = "identifier";
///////////////////////////////////////////////////////////////////////
// Grammar
expr =
additive_expr.alias()
///////////////////////////////////////////////////////////////////////
inline auto const expr_def =
additive_expr
;
additive_expr =
inline auto const additive_expr_def =
multiplicative_expr
>> *(additive_op > multiplicative_expr)
;
;
multiplicative_expr =
inline auto const multiplicative_expr_def =
unary_expr
>> *(multiplicative_op > unary_expr)
;
;
unary_expr =
inline auto const unary_expr_def =
primary_expr
| (unary_op > primary_expr)
;
primary_expr =
inline auto const primary_expr_def =
double_
| identifier
| fun_tok > '(' > expr > ')'
@@ -140,36 +163,13 @@ namespace mupp { namespace parser
| '(' > expr > ')'
;
identifier =
inline auto const identifier_def =
raw[lexeme['$' >> *(alnum | '_')]]
;
// name all the rules
additive_expr.name("additive_expr");
multiplicative_expr.name("multiplicative_expr");
unary_expr.name("unary_expr");
primary_expr.name("primary_expr");
identifier.name("identifier");
BOOST_SPIRIT_DEFINE(expr, additive_expr, multiplicative_expr,
unary_expr, primary_expr, identifier)
// Debugging and error handling and reporting support.
BOOST_SPIRIT_DEBUG_NODES(
(expr)
(additive_expr)
(multiplicative_expr)
(unary_expr)
(primary_expr)
(identifier)
);
// Error handling: on error in expr, call error_handler.
on_error<fail>(expr,
error_handler_function(error_handler)(
"**ERROR** Expecting ", _4, _3));
// Annotation: on success in primary_expr, call annotation.
on_success(primary_expr,
annotation_function(error_handler.iters)(_val, _1));
}
}}

View File

@@ -33,12 +33,12 @@
#ifndef _PSKIPPER_HPP_
#define _PSKIPPER_HPP_
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/home/x3.hpp>
namespace mupp { namespace parser
{
namespace qi = boost::spirit::qi;
namespace ascii = boost::spirit::ascii;
namespace x3 = boost::spirit::x3;
namespace ascii = boost::spirit::x3::ascii;
///////////////////////////////////////////////////////////////////////////////
/**
@@ -61,38 +61,22 @@ namespace mupp { namespace parser
* /* This is a
* multi-line comment * /
* @endcode
*
* @tparam Iterator the iterator type for the input (typically std::string::const_iterator)
*/
///////////////////////////////////////////////////////////////////////////////
template <typename Iterator>
struct PSkipper : qi::grammar<Iterator>
{
/**
* @brief Constructor that initializes the skipper grammar rules.
*
* Sets up the grammar to recognize whitespace and various comment styles.
*/
PSkipper() : PSkipper::base_type(start)
{
qi::char_type char_;
qi::lit_type lit;
qi::eol_type eol;
ascii::space_type space;
ascii::print_type print;
single_line_comment = (lit('%') | lit('#') | lit("//")) >> *print >> eol;
using x3::char_;
using x3::lit;
using x3::eol;
using ascii::space;
using ascii::print;
start =
space // tab/space/cr/lf
| "/*" >> *(char_ - "*/") >> "*/" // C-style comments
| single_line_comment
;
}
inline auto const single_line_comment = (lit('%') | lit('#') | lit("//")) >> *print >> eol;
qi::rule<Iterator> single_line_comment; ///< Rule for single-line comments (%, #, //)
qi::rule<Iterator> start; ///< Top-level skipper rule
};
inline auto const skipper =
space // tab/space/cr/lf
| "/*" >> *(char_ - "*/") >> "*/" // C-style comments
| single_line_comment
;
}}
#endif // _PSKIPPER_HPP_

View File

@@ -59,25 +59,23 @@ namespace mupp { namespace parser
*
* The grammar parses a statement list (one or more statements) and builds
* an AST that can be analyzed and evaluated by the semantic analyzer.
*
* @tparam Iterator the iterator type for the input (typically std::string::const_iterator)
*/
///////////////////////////////////////////////////////////////////////////////
template <typename Iterator>
struct PStatement : qi::grammar<Iterator, ast::statement_list(), PSkipper<Iterator> >
{
/**
* @brief Constructor that initializes the statement grammar rules.
* @param error_handler reference to the error handler for reporting parse errors
*/
PStatement(PErrorHandler<Iterator>& error_handler);
PExpression<Iterator> expr; ///< Expression grammar for parsing right-hand sides
qi::rule<Iterator, ast::statement_list(), PSkipper<Iterator> > statement_list; ///< Rule for parsing a list of statements
qi::rule<Iterator, ast::variable_declaration(), PSkipper<Iterator> > variable_declaration; ///< Rule for variable declarations
qi::rule<Iterator, ast::assignment(), PSkipper<Iterator> > assignment; ///< Rule for assignment statements
qi::rule<Iterator, std::string(), PSkipper<Iterator> > identifier; ///< Rule for identifiers (without '$' prefix)
};
// Rule IDs
struct statement_list_class;
struct variable_declaration_class;
struct assignment_class;
struct stmt_identifier_class;
// Rule declarations
typedef x3::rule<statement_list_class, ast::statement_list> statement_list_type;
typedef x3::rule<variable_declaration_class, ast::variable_declaration> variable_declaration_type;
typedef x3::rule<assignment_class, ast::assignment> assignment_type;
typedef x3::rule<stmt_identifier_class, std::string> stmt_identifier_type;
BOOST_SPIRIT_DECLARE(statement_list_type, variable_declaration_type,
assignment_type, stmt_identifier_type)
}}
#endif // _PSTATEMENT_HPP_

View File

@@ -9,8 +9,8 @@
see https://github.com/boostorg/spirit
This file contains the implementation (definition) of the PStatement
template grammar. It defines the grammar rules for parsing variable
declarations and assignments using Boost.Spirit Qi.
grammar. It defines the grammar rules for parsing variable
declarations and assignments using Boost.Spirit X3.
The grammar supports:
- Variable declarations: var <identifier> = <expression>
@@ -43,77 +43,55 @@
***************************************************************************/
#include "PStatement.hpp"
#include "PExpressionDef.hpp"
#include "PErrorHandler.hpp"
#include "PAnnotation.hpp"
namespace mupp { namespace parser
{
template <typename Iterator>
PStatement<Iterator>::PStatement(PErrorHandler<Iterator>& error_handler)
: PStatement::base_type(statement_list), expr(error_handler)
{
qi::_1_type _1;
qi::_2_type _2;
qi::_3_type _3;
qi::_4_type _4;
namespace x3 = boost::spirit::x3;
namespace ascii = boost::spirit::x3::ascii;
qi::_val_type _val;
qi::raw_type raw;
qi::lexeme_type lexeme;
qi::alpha_type alpha;
qi::alnum_type alnum;
using x3::raw;
using x3::lexeme;
using ascii::alpha;
using ascii::alnum;
using qi::on_error;
using qi::on_success;
using qi::fail;
using boost::phoenix::function;
///////////////////////////////////////////////////////////////////////
// Rule definitions
///////////////////////////////////////////////////////////////////////
typedef function<mupp::PErrorHandler<Iterator> > error_handler_function;
typedef function<mupp::PAnnotation<Iterator> > annotation_function;
inline statement_list_type const statement_list = "statement_list";
inline variable_declaration_type const variable_declaration = "variable_declaration";
inline assignment_type const assignment = "assignment";
inline stmt_identifier_type const stmt_identifier = "identifier";
statement_list =
///////////////////////////////////////////////////////////////////////
// Grammar
///////////////////////////////////////////////////////////////////////
inline auto const statement_list_def =
+(variable_declaration | assignment)
;
identifier =
inline auto const stmt_identifier_def =
raw[lexeme[(alpha | '_') >> *(alnum | '_')]]
;
variable_declaration =
inline auto const variable_declaration_def =
lexeme["var" >> !(alnum | '_')] // make sure we have whole words
> identifier
> stmt_identifier
> -('=' > expr)
;
assignment =
identifier
inline auto const assignment_def =
stmt_identifier
> '='
> expr
;
// name all the rules
statement_list.name("statement_list");
identifier.name("identifier");
variable_declaration.name("variable_declaration");
assignment.name("assignment");
BOOST_SPIRIT_DEFINE(statement_list, variable_declaration, assignment, stmt_identifier)
// Debugging and error handling and reporting support.
BOOST_SPIRIT_DEBUG_NODES(
(statement_list)
(identifier)
(variable_declaration)
(assignment)
);
// Error handling: on error in statement_list, call error_handler.
on_error<fail>(statement_list,
error_handler_function(error_handler)(
"**ERROR** Expecting ", _4, _3));
// Annotation: on success in assignment, call annotation.
on_success(assignment,
annotation_function(error_handler.iters)(_val, _1));
}
}}

View File

@@ -8,8 +8,8 @@
Based on Joel de Guzman example on calc7,
see https://github.com/boostorg/spirit
This file explicitly instantiates the PExpression template grammar for
std::string::const_iterator. Template instantiation in a separate
This file instantiates the PExpression grammar rules for
std::string::const_iterator. Instantiation in a separate
compilation unit reduces compile times and allows the grammar
implementation to be hidden from clients.
@@ -35,11 +35,4 @@
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#if defined(_MSC_VER)
# pragma warning(disable: 4345)
#endif
#include "PExpressionDef.hpp"
typedef std::string::const_iterator iterator_type;
template struct mupp::parser::PExpression<iterator_type>;

View File

@@ -8,8 +8,8 @@
Based on Joel de Guzman example on calc7,
see https://github.com/boostorg/spirit
This file explicitly instantiates the PStatement template grammar for
std::string::const_iterator. Template instantiation in a separate
This file instantiates the PStatement grammar rules for
std::string::const_iterator. Instantiation in a separate
compilation unit reduces compile times and allows the grammar
implementation to be hidden from clients.
@@ -35,11 +35,4 @@
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#if defined(_MSC_VER)
# pragma warning(disable: 4345)
#endif
#include "PStatementDef.hpp"
typedef std::string::const_iterator iterator_type;
template struct mupp::parser::PStatement<iterator_type>;

View File

@@ -34,8 +34,11 @@
#include "PSkipper.hpp"
#include "PErrorHandler.hpp"
#include "PStatement.hpp"
#include "PStatementDef.hpp"
#include "PProgram.hpp"
#include <boost/spirit/home/x3.hpp>
//--------------------------------------------------------------------------
/**
* @brief Default constructor creating an invalid handler.
@@ -78,12 +81,11 @@ PVarHandler::PVarHandler(PmuppCollection *coll, std::string parse_str, std::stri
iterator_type end = fParseStr.end();
mupp::PErrorHandler<iterator_type> error_handler(iter, end); // the error handler
mupp::parser::PStatement<iterator_type> parser(error_handler); // the parser
mupp::prog::PProgram prog(error_handler); // our compiler, and exec
mupp::parser::PSkipper<iterator_type> skipper; // the skipper parser
// perform the parsing
bool success = phrase_parse(iter, end, parser, skipper, fAst);
namespace x3 = boost::spirit::x3;
bool success = x3::phrase_parse(iter, end, mupp::parser::statement_list, mupp::parser::skipper, fAst);
if (success && iter == end) {
if (prog(fAst)) { // semantic analysis
std::vector<double> data, dataErr;