6 Commits
root6 ... x3

19 changed files with 1392 additions and 1325 deletions

View File

@@ -232,13 +232,16 @@ install(
#--- install headers ---------------------------------------------------------- #--- install headers ----------------------------------------------------------
install( install(
FILES ${MUSRFIT_INC}/PFitterFcn.h FILES ${MUSRFIT_INC}/PFindRun.h
${MUSRFIT_INC}/PFitterFcn.h
${MUSRFIT_INC}/PFitter.h ${MUSRFIT_INC}/PFitter.h
${MUSRFIT_INC}/PFourierCanvas.h ${MUSRFIT_INC}/PFourierCanvas.h
${MUSRFIT_INC}/PFourier.h ${MUSRFIT_INC}/PFourier.h
${MUSRFIT_INC}/PFunctionAst.h
${MUSRFIT_INC}/PFunctionGrammar.h ${MUSRFIT_INC}/PFunctionGrammar.h
${MUSRFIT_INC}/PFunction.h ${MUSRFIT_INC}/PFunction.h
${MUSRFIT_INC}/PFunctionHandler.h ${MUSRFIT_INC}/PFunctionHandler.h
${MUSRFIT_INC}/PMsgBox.h
${MUSRFIT_INC}/PMsr2Data.h ${MUSRFIT_INC}/PMsr2Data.h
${MUSRFIT_INC}/PMsrHandler.h ${MUSRFIT_INC}/PMsrHandler.h
${MUSRFIT_INC}/PMusrCanvas.h ${MUSRFIT_INC}/PMusrCanvas.h

File diff suppressed because it is too large Load Diff

View File

@@ -30,8 +30,12 @@
#include <string> #include <string>
#include <cassert> #include <cassert>
#include <boost/spirit/home/x3.hpp>
#include "PFunctionHandler.h" #include "PFunctionHandler.h"
namespace x3 = boost::spirit::x3;
//------------------------------------------------------------- //-------------------------------------------------------------
// Constructor // Constructor
//------------------------------------------------------------- //-------------------------------------------------------------
@@ -67,7 +71,6 @@ PFunctionHandler::~PFunctionHandler()
Bool_t PFunctionHandler::DoParse() Bool_t PFunctionHandler::DoParse()
{ {
Bool_t success = true; Bool_t success = true;
PFunctionGrammar function;
TString line; TString line;
// feed the function block into the parser. Start with i=1, since i=0 is FUNCTIONS // feed the function block into the parser. Start with i=1, since i=0 is FUNCTIONS
@@ -89,14 +92,32 @@ Bool_t PFunctionHandler::DoParse()
} }
line.ToUpper(); line.ToUpper();
// do parsing // do parsing with X3
tree_parse_info<> info = ast_parse(line.Data(), function, space_p); musrfit::ast::assignment assignment;
std::string str(line.Data());
auto iter = str.begin();
auto end = str.end();
if (info.full) { // parsing successful // Get the X3 grammar
PFunction func(info); // generate an evaluation function object based on the AST tree auto const& grammar = musrfit::function_grammar();
fFuncs.push_back(func); // feeds it to the functions vector
} else { try {
bool parseSuccess = x3::phrase_parse(iter, end, grammar, x3::space, assignment);
if (parseSuccess && iter == end) { // parsing successful
PFunction func(assignment); // generate an evaluation function object based on the AST
fFuncs.push_back(func); // feeds it to the functions vector
} else {
std::cerr << std::endl << "**ERROR**: FUNCTIONS parse failed in line " << fLines[i].fLineNo << std::endl;
if (iter != end) {
std::cerr << "**ERROR**: Stopped at: " << std::string(iter, end) << std::endl;
}
success = false;
break;
}
} catch (x3::expectation_failure<std::string::iterator> const& e) {
std::cerr << std::endl << "**ERROR**: FUNCTIONS parse failed in line " << fLines[i].fLineNo << std::endl; std::cerr << std::endl << "**ERROR**: FUNCTIONS parse failed in line " << fLines[i].fLineNo << std::endl;
std::cerr << "**ERROR**: Expected: " << e.which() << std::endl;
success = false; success = false;
break; break;
} }

View File

@@ -8,7 +8,7 @@
***************************************************************************/ ***************************************************************************/
/*************************************************************************** /***************************************************************************
* Copyright (C) 2007-2025 by Andreas Suter * * Copyright (C) 2007-2026 by Andreas Suter *
* andreas.suter@psi.ch * * andreas.suter@psi.ch *
* * * *
* This program is free software; you can redistribute it and/or modify * * This program is free software; you can redistribute it and/or modify *
@@ -33,85 +33,23 @@
#include <vector> #include <vector>
#include <boost/version.hpp> #include <boost/version.hpp>
#include <boost/variant/static_visitor.hpp>
#if BOOST_VERSION >= 103800 #include <boost/variant/apply_visitor.hpp>
# include <boost/spirit/include/classic_ast.hpp>
using namespace BOOST_SPIRIT_CLASSIC_NS;
#else
# include <boost/spirit/tree/ast.hpp>
using namespace boost::spirit;
#endif
#include <TString.h> #include <TString.h>
#include "PMusr.h" #include "PMusr.h"
#include "PFunctionAst.h"
#include "PFunctionGrammar.h" #include "PFunctionGrammar.h"
//----------------------------------------------------------------------------
// Operator tags for arithmetic operations
//----------------------------------------------------------------------------
#define OP_ADD 0 ///< Addition operator tag
#define OP_SUB 1 ///< Subtraction operator tag
#define OP_MUL 2 ///< Multiplication operator tag
#define OP_DIV 3 ///< Division operator tag
//----------------------------------------------------------------------------
// Function tags for mathematical functions
//----------------------------------------------------------------------------
#define FUN_COS 0 ///< Cosine function tag
#define FUN_SIN 1 ///< Sine function tag
#define FUN_TAN 2 ///< Tangent function tag
#define FUN_COSH 3 ///< Hyperbolic cosine function tag
#define FUN_SINH 4 ///< Hyperbolic sine function tag
#define FUN_TANH 5 ///< Hyperbolic tangent function tag
#define FUN_ACOS 6 ///< Inverse cosine (arccos) function tag
#define FUN_ASIN 7 ///< Inverse sine (arcsin) function tag
#define FUN_ATAN 8 ///< Inverse tangent (arctan) function tag
#define FUN_ACOSH 9 ///< Inverse hyperbolic cosine function tag
#define FUN_ASINH 10 ///< Inverse hyperbolic sine function tag
#define FUN_ATANH 11 ///< Inverse hyperbolic tangent function tag
#define FUN_LOG 12 ///< Base-10 logarithm function tag
#define FUN_LN 13 ///< Natural logarithm function tag
#define FUN_EXP 14 ///< Exponential function tag
#define FUN_SQRT 15 ///< Square root function tag
#define FUN_POW 16 ///< Power function tag (base^exponent)
//----------------------------------------------------------------------------
/**
* \brief Tree node structure for efficient function evaluation.
*
* This structure represents a node in the evaluation tree used to compute
* function values. The abstract syntax tree (AST) generated by the parser
* is converted into this more efficient tree structure for faster evaluation.
*
* Each node can represent:
* - A leaf node (constant, parameter, map reference)
* - An operator node (arithmetic operation)
* - A function node (mathematical function)
*
* The tree is evaluated recursively by traversing from the root to the leaves.
*
* \see PFunction::EvalNode for the recursive evaluation algorithm
*/
typedef struct func_tree_node {
Int_t fID; ///< Node type identifier (from PFunctionGrammar constants)
Int_t fOperatorTag; ///< Operator type: OP_ADD, OP_SUB, OP_MUL, OP_DIV
Int_t fFunctionTag; ///< Function type: FUN_COS, FUN_SIN, FUN_EXP, etc.
Int_t fIvalue; ///< Integer value for parameter numbers, map indices, or temperature indices
Bool_t fSign; ///< Sign flag: true for negative, false for positive
Double_t fDvalue; ///< Numeric value for constants and real number literals
std::vector<func_tree_node> children; ///< Child nodes forming the sub-tree
} PFuncTreeNode;
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
/** /**
* \brief Class for parsing and evaluating mathematical functions from msr-file FUNCTIONS blocks. * \brief Class for parsing and evaluating mathematical functions from msr-file FUNCTIONS blocks.
* *
* This class handles the complete lifecycle of a function definition: * This class handles the complete lifecycle of a function definition:
* 1. Parses the function string using PFunctionGrammar into an AST * 1. Parses the function string using PFunctionGrammar into an AST
* 2. Converts the AST into an efficient evaluation tree * 2. Validates parameter and map references
* 3. Validates parameter and map references * 3. Evaluates the function with given parameters and metadata using the visitor pattern
* 4. Evaluates the function with given parameters and metadata
* *
* Functions can reference: * Functions can reference:
* - Fit parameters (PAR1, PAR2, ...) * - Fit parameters (PAR1, PAR2, ...)
@@ -126,7 +64,7 @@ typedef struct func_tree_node {
* \endcode * \endcode
* *
* \see PFunctionGrammar for the grammar definition * \see PFunctionGrammar for the grammar definition
* \see PFuncTreeNode for the evaluation tree structure * \see musrfit::ast for the AST structure
*/ */
class PFunction { class PFunction {
public: public:
@@ -134,13 +72,13 @@ class PFunction {
/** /**
* \brief Constructor that parses and prepares a function for evaluation. * \brief Constructor that parses and prepares a function for evaluation.
* *
* \param info Abstract syntax tree (AST) from parsing a function expression * \param assignment Abstract syntax tree (AST) from parsing a function expression
*/ */
PFunction(tree_parse_info<> info); PFunction(const musrfit::ast::assignment& assignment);
//------------------------------------------------------------------------ //------------------------------------------------------------------------
/** /**
* \brief Destructor that cleans up the evaluation tree. * \brief Destructor that cleans up resources.
*/ */
virtual ~PFunction(); virtual ~PFunction();
@@ -202,101 +140,72 @@ class PFunction {
protected: protected:
//------------------------------------------------------------------------ //------------------------------------------------------------------------
/** /**
* \brief Initializes all fields of an evaluation tree node to default values. * \brief Recursively validates parameter and map references in the AST.
* *
* \param node Node to initialize * \param operand Current operand being checked
*/
virtual void InitNode(PFuncTreeNode &node);
//------------------------------------------------------------------------
/**
* \brief Extracts the function number from the AST.
*
* Parses the function label (FUN#) to extract the numeric identifier.
*
* \return true if successful, false otherwise
*/
virtual Bool_t SetFuncNo();
//------------------------------------------------------------------------
/**
* \brief Recursively validates parameter and map references in the tree.
*
* \param node Current node being checked
* \param mapSize Number of available map entries * \param mapSize Number of available map entries
* \param paramSize Number of available fit parameters * \param paramSize Number of available fit parameters
* \return true if all references are valid, false otherwise * \return true if all references are valid, false otherwise
*/ */
virtual Bool_t FindAndCheckMapAndParamRange(PFuncTreeNode &node, UInt_t mapSize, UInt_t paramSize); virtual Bool_t FindAndCheckMapAndParamRange(const musrfit::ast::operand& operand,
UInt_t mapSize, UInt_t paramSize);
//------------------------------------------------------------------------ //------------------------------------------------------------------------
/** /**
* \brief Initiates the conversion from AST to evaluation tree. * \brief Generates a human-readable string representation of the AST.
* *
* \return true if successful, false otherwise * \param expr Expression AST to convert to string
* \return Formatted string representation
*/ */
virtual Bool_t GenerateFuncEvalTree(); virtual TString GenerateString(const musrfit::ast::expression& expr);
//------------------------------------------------------------------------ //------------------------------------------------------------------------
/** /**
* \brief Recursively builds the evaluation tree from the AST. * \brief Generates a string representation of an operand.
* *
* \param i Iterator pointing to current AST node * \param operand Operand AST to convert to string
* \param node Evaluation tree node to fill * \return Formatted string representation
*/ */
virtual void FillFuncEvalTree(iter_t const& i, PFuncTreeNode &node); virtual TString GenerateStringOperand(const musrfit::ast::operand& operand);
//------------------------------------------------------------------------
/**
* \brief Recursively evaluates an evaluation tree node.
*
* \param node Node to evaluate
* \return Computed value for this node and its subtree
*/
virtual Double_t EvalNode(PFuncTreeNode &node);
//------------------------------------------------------------------------
/**
* \brief Initiates cleanup of the evaluation tree.
*/
virtual void CleanupFuncEvalTree();
//------------------------------------------------------------------------
/**
* \brief Recursively cleans up evaluation tree nodes and their children.
*
* \param node Node to clean up
*/
virtual void CleanupNode(PFuncTreeNode &node);
private: private:
tree_parse_info<> fInfo; ///< AST parse tree from Boost.Spirit parser
std::vector<Double_t> fParam; ///< Current fit parameter values for evaluation
std::vector<Int_t> fMap; ///< Map vector for indirect parameter references
PFuncTreeNode fFunc; ///< Root node of the evaluation tree
Bool_t fValid; ///< Validity flag: true if function parsed and initialized successfully
Int_t fFuncNo; ///< Function number extracted from label (x in FUNx)
//------------------------------------------------------------------------
/** /**
* \brief Initiates generation of human-readable function string. * \brief Visitor class for evaluating the AST.
* *
* \param info AST parse tree to convert to string * This visitor traverses the AST and computes the numeric value of the expression.
* It uses the visitor pattern with boost::static_visitor to handle different
* AST node types.
*/ */
virtual void EvalTreeForString(tree_parse_info<> info); class EvalVisitor : public boost::static_visitor<Double_t>
{
public:
EvalVisitor(const std::vector<Int_t>& map,
const std::vector<Double_t>& param,
const PMetaData& metaData)
: fMap(map), fParam(param), fMetaData(metaData) {}
//------------------------------------------------------------------------ Double_t operator()(const musrfit::ast::leer&) const;
/** Double_t operator()(double val) const;
* \brief Recursively generates formatted function string from AST. Double_t operator()(const musrfit::ast::constant& c) const;
* Double_t operator()(const musrfit::ast::parameter& p) const;
* \param i Iterator pointing to current AST node Double_t operator()(const musrfit::ast::map_ref& m) const;
* \param funcFlag Flag indicating if currently inside a function call Double_t operator()(const musrfit::ast::function_call& f) const;
*/ Double_t operator()(const musrfit::ast::power_call& p) const;
virtual void EvalTreeForStringExpression(iter_t const& i, bool funcFlag=false); Double_t operator()(const musrfit::ast::expression& expr) const;
private:
const std::vector<Int_t>& fMap;
const std::vector<Double_t>& fParam;
const PMetaData& fMetaData;
};
musrfit::ast::expression fAst; ///< Abstract syntax tree for the expression
std::vector<Double_t> fParam; ///< Current fit parameter values for evaluation
std::vector<Int_t> fMap; ///< Map vector for indirect parameter references
Bool_t fValid; ///< Validity flag: true if function parsed and initialized successfully
Int_t fFuncNo; ///< Function number extracted from label (x in FUNx)
TString fFuncString; ///< Formatted, human-readable function representation TString fFuncString; ///< Formatted, human-readable function representation
PMetaData fMetaData; ///< Metadata from experimental data (field, energy, temperature, etc.) PMetaData fMetaData; ///< Metadata from experimental data (field, energy, temperature, etc.)
}; };

306
src/include/PFunctionAst.h Normal file
View File

@@ -0,0 +1,306 @@
/***************************************************************************
PFunctionAst.h
Author: Andreas Suter
e-mail: andreas.suter@psi.ch
Abstract Syntax Tree (AST) definitions for Spirit X3 parser.
Migrated from Qi to X3 for improved compile times and cleaner syntax.
***************************************************************************/
/***************************************************************************
* Copyright (C) 2007-2026 by Andreas Suter *
* andreas.suter@psi.ch *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#ifndef _PFUNCTIONAST_H_
#define _PFUNCTIONAST_H_
#include <boost/config/warning_disable.hpp>
#include <boost/variant/recursive_variant.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/fusion/include/io.hpp>
#include <boost/optional.hpp>
#include <list>
namespace musrfit { namespace ast
{
///////////////////////////////////////////////////////////////////////////
/**
* @brief Abstract Syntax Tree (AST) definitions for the function parser.
*
* This namespace defines the complete AST structure used to represent
* parsed function expressions. The AST is built using Boost.Variant for
* type-safe unions and Boost.Spirit X3's automatic AST generation from
* grammar rules.
*
* The AST supports:
* - Arithmetic operations: +, -, *, /
* - Mathematical functions: cos, sin, tan, exp, log, sqrt, pow, etc.
* - Constants: PI, GAMMA_MU, field (B), energy (EN), temperature (T#)
* - Parameter references: PAR#, -PAR#
* - Map references: MAP#
* - Function labels: FUN#
* - Expression evaluation with proper precedence
*/
///////////////////////////////////////////////////////////////////////////
/**
* @brief Enumeration of arithmetic operators.
*
* These tokens represent the fundamental arithmetic operations supported
* by the expression evaluator.
*/
enum optoken
{
op_plus, ///< Addition operator (+)
op_minus, ///< Subtraction operator (-)
op_times, ///< Multiplication operator (*)
op_divide, ///< Division operator (/)
};
/**
* @brief Enumeration of supported mathematical functions.
*
* These function identifiers map to standard mathematical operations
* evaluated during the semantic analysis phase.
*/
enum funid
{
fun_cos, ///< Cosine function
fun_sin, ///< Sine function
fun_tan, ///< Tangent function
fun_cosh, ///< Hyperbolic cosine function
fun_sinh, ///< Hyperbolic sine function
fun_tanh, ///< Hyperbolic tangent function
fun_acos, ///< Arccosine function
fun_asin, ///< Arcsine function
fun_atan, ///< Arctangent function
fun_acosh, ///< Inverse hyperbolic cosine function
fun_asinh, ///< Inverse hyperbolic sine function
fun_atanh, ///< Inverse hyperbolic tangent function
fun_log, ///< Base-10 logarithm function
fun_ln, ///< Natural logarithm function
fun_exp, ///< Exponential function
fun_sqrt ///< Square root function
};
/**
* @brief Represents an empty/null AST node.
*
* Used as a placeholder in variant types where no value is present.
*/
struct leer {};
// Forward declarations for recursive AST structures
struct expression;
struct function_call;
struct power_call;
/**
* @brief Represents a constant value in an expression.
*
* Constants can be:
* - Mathematical constants: PI, GAMMA_MU
* - Metadata values: B (field), EN (energy), T# (temperature)
*/
struct constant
{
/**
* @brief Enumeration of constant types.
*/
enum type {
pi, ///< Mathematical constant π (3.14159...)
gamma_mu, ///< Muon gyromagnetic ratio constant
field, ///< Magnetic field from experimental data (B or -B)
energy, ///< Energy from experimental data (EN or -EN)
temp ///< Temperature from experimental data (T# or -T#)
};
type const_type; ///< The type of constant
bool sign; ///< Sign flag: false for positive, true for negative
int index; ///< Index for temperature (T0, T1, T2, ...)
};
/**
* @brief Represents a parameter reference in an expression.
*
* Parameters are fit parameters referenced as PAR# or -PAR#,
* where # is the parameter number (1-based).
*/
struct parameter
{
int number; ///< Parameter number (extracted from PAR#)
bool sign; ///< Sign flag: false for PAR#, true for -PAR#
};
/**
* @brief Represents a map reference in an expression.
*
* Maps provide indirect parameter references: MAP# or -MAP#
* references the map vector at index #-1, which then indexes
* into the parameter vector.
*/
struct map_ref
{
int number; ///< Map number (extracted from MAP#)
bool sign; ///< Sign flag: false for MAP#, true for -MAP#
};
/**
* @brief Variant type representing any operand in an expression.
*
* An operand can be a literal number, constant, parameter, map reference,
* function call, power operation, or parenthesized expression. The variant
* uses recursive_wrapper for types that contain expressions to handle
* recursive grammar structures.
*/
typedef boost::variant<
leer ///< Empty placeholder
, double ///< Numeric literal
, constant ///< Constant value
, parameter ///< Parameter reference
, map_ref ///< Map reference
, boost::recursive_wrapper<function_call> ///< Function call (recursive)
, boost::recursive_wrapper<power_call> ///< Power operation (recursive)
, boost::recursive_wrapper<expression> ///< Parenthesized expression (recursive)
>
operand;
/**
* @brief Represents a binary operation with an operator and right operand.
*
* Used in expression chains where the left operand is the accumulated
* result from previous operations.
*/
struct operation
{
optoken operator_; ///< The binary operator (+, -, *, /)
operand operand_; ///< The right-hand operand
};
/**
* @brief Represents a complete expression with operator precedence.
*
* Expressions are built as a first operand followed by a sequence of
* operations. This structure naturally encodes operator precedence as
* determined by the grammar rules.
*/
struct expression
{
operand first; ///< The first operand in the expression
std::list<operation> rest; ///< Sequence of operations applied left-to-right
};
/**
* @brief Represents a function call with a single argument.
*
* Examples: COS(PAR1), SQRT(B), EXP(-PAR2 * T0)
*/
struct function_call
{
funid func_id; ///< The function identifier
expression arg; ///< The argument expression
};
/**
* @brief Represents a power operation.
*
* Syntax: POW(base, exponent)
* Both base and exponent are full expressions.
*/
struct power_call
{
expression base; ///< The base expression
expression pow; ///< The exponent expression
};
/**
* @brief Represents an assignment statement.
*
* Syntax: FUN# = expression
* Assigns the result of evaluating the right-hand expression to a
* function label.
*/
struct assignment
{
int func_number; ///< Function number (extracted from FUN#)
expression rhs; ///< The right-hand side expression to evaluate
};
/**
* @brief Stream output operator for leer nodes (debugging support).
* @param out the output stream
* @return the output stream for chaining
*/
inline std::ostream& operator<<(std::ostream& out, leer) { out << std::string("leer"); return out; }
}}
// Boost.Fusion adaptations to make structures compatible with X3 attribute propagation
BOOST_FUSION_ADAPT_STRUCT(
musrfit::ast::constant,
(musrfit::ast::constant::type, const_type)
(bool, sign)
(int, index)
)
BOOST_FUSION_ADAPT_STRUCT(
musrfit::ast::parameter,
(int, number)
(bool, sign)
)
BOOST_FUSION_ADAPT_STRUCT(
musrfit::ast::map_ref,
(int, number)
(bool, sign)
)
BOOST_FUSION_ADAPT_STRUCT(
musrfit::ast::operation,
(musrfit::ast::optoken, operator_)
(musrfit::ast::operand, operand_)
)
BOOST_FUSION_ADAPT_STRUCT(
musrfit::ast::expression,
(musrfit::ast::operand, first)
(std::list<musrfit::ast::operation>, rest)
)
BOOST_FUSION_ADAPT_STRUCT(
musrfit::ast::function_call,
(musrfit::ast::funid, func_id)
(musrfit::ast::expression, arg)
)
BOOST_FUSION_ADAPT_STRUCT(
musrfit::ast::power_call,
(musrfit::ast::expression, base)
(musrfit::ast::expression, pow)
)
BOOST_FUSION_ADAPT_STRUCT(
musrfit::ast::assignment,
(int, func_number)
(musrfit::ast::expression, rhs)
)
#endif // _PFUNCTIONAST_H_

View File

@@ -1,14 +1,17 @@
/*************************************************************************** /***************************************************************************
PFunctionGrammer.h PFunctionGrammar.h
Author: Andreas Suter Author: Andreas Suter
e-mail: andreas.suter@psi.ch e-mail: andreas.suter@psi.ch
Header-only grammar for parsing function entries in msr-file FUNCTION blocks.
This version uses Boost.Spirit X3 in header-only mode for maximum compatibility.
***************************************************************************/ ***************************************************************************/
/*************************************************************************** /***************************************************************************
* Copyright (C) 2007-2025 by Andreas Suter * * Copyright (C) 2007-2026 by Andreas Suter *
* andreas.suter@psi.ch * * andreas.suter@psi.ch *
* * * *
* This program is free software; you can redistribute it and/or modify * * This program is free software; you can redistribute it and/or modify *
@@ -30,223 +33,296 @@
#ifndef _PFUNCTIONGRAMMAR_H_ #ifndef _PFUNCTIONGRAMMAR_H_
#define _PFUNCTIONGRAMMAR_H_ #define _PFUNCTIONGRAMMAR_H_
//#define BOOST_SPIRIT_DEBUG // Check Boost version - require 1.61+ for Spirit X3
#include <boost/version.hpp> #include <boost/version.hpp>
#if BOOST_VERSION < 106100
#if BOOST_VERSION >= 103800 # error "Boost version 1.61.0 or higher is required for Spirit X3. Please upgrade Boost."
# include <boost/spirit/include/classic_core.hpp>
# include <boost/spirit/include/classic_ast.hpp>
using namespace BOOST_SPIRIT_CLASSIC_NS;
#else
# include <boost/spirit/core.hpp>
# include <boost/spirit/tree/ast.hpp>
using namespace boost::spirit;
#endif #endif
//-------------------------------------------------------------------------- #include "PFunctionAst.h"
/** #include <boost/spirit/home/x3.hpp>
* \brief Iterator type for parsing characters.
*/
typedef char const* iterator_t;
//-------------------------------------------------------------------------- namespace x3 = boost::spirit::x3;
/**
* \brief Type for parse tree matching results.
*/
typedef tree_match<iterator_t> parse_tree_match_t;
//--------------------------------------------------------------------------
/** /**
* \brief Iterator type for traversing the parse tree. * @namespace musrfit::grammar
* @brief Boost.Spirit X3 grammar definitions for parsing function expressions.
*
* This namespace contains the complete grammar for parsing msr-file FUNCTION
* block entries. The grammar supports arithmetic expressions with operator
* precedence, mathematical functions, constants, and parameter/map references.
*
* The grammar is header-only for maximum portability and uses X3's modern
* attribute propagation system to automatically build the AST during parsing.
*/ */
typedef parse_tree_match_t::tree_iterator iter_t; namespace musrfit { namespace grammar
//--------------------------------------------------------------------------
/**
* \brief EBNF-like grammar definition for parsing function entries in msr-file FUNCTION blocks.
*
* This grammar defines the syntax for parsing mathematical function expressions in msr-files.
* It supports:
* - Basic arithmetic operations (+, -, *, /)
* - Mathematical functions (trigonometric, hyperbolic, logarithmic, exponential, etc.)
* - Constants (PI, GAMMA_MU, field B, energy EN, temperature T)
* - Parameters (PAR) and maps (MAP)
* - Function references (FUN)
*
* The grammar follows an EBNF-like structure with the following hierarchy:
* \verbatim
* assignment = fun_label '=' expression
* expression = term { ('+' | '-') term }
* term = factor { ('*' | '/') factor }
* factor = real | constant | parameter | map | function | power | '(' expression ')'
* \endverbatim
*
* \see PFunction for the class that uses this grammar to evaluate parsed expressions
*/
struct PFunctionGrammar : public grammar<PFunctionGrammar>
{ {
static const int realID = 1; ///< Identifier for real number literals using x3::int_;
static const int constPiID = 2; ///< Identifier for PI constant using x3::double_;
static const int constGammaMuID = 3; ///< Identifier for GAMMA_MU constant (muon gyromagnetic ratio) using x3::lit;
static const int constFieldID = 4; ///< Identifier for magnetic field constant (B or -B) using x3::lexeme;
static const int constEnergyID = 5; ///< Identifier for energy constant (EN or -EN) using x3::attr;
static const int constTempID = 6; ///< Identifier for temperature constant (T# or -T#)
static const int funLabelID = 7; ///< Identifier for function label (FUN#)
static const int parameterID = 8; ///< Identifier for parameter reference (PAR# or -PAR#)
static const int mapID = 9; ///< Identifier for map reference (MAP#)
static const int functionID = 10; ///< Identifier for mathematical functions (cos, sin, exp, etc.)
static const int powerID = 11; ///< Identifier for power function (POW)
static const int factorID = 12; ///< Identifier for factor in expression
static const int termID = 13; ///< Identifier for term in expression
static const int expressionID = 14; ///< Identifier for expression
static const int assignmentID = 15; ///< Identifier for assignment statement
//------------------------------------------------------------------------ ///////////////////////////////////////////////////////////////////////////
/** // Symbol tables - using inline to avoid multiple definition errors
* \brief Inner template structure defining the grammar rules. ///////////////////////////////////////////////////////////////////////////
*
* This template structure contains the actual grammar rule definitions /**
* using Boost.Spirit syntax. It defines how the parser should recognize * @brief Symbol table for additive operators (+ and -).
* and build an abstract syntax tree (AST) for function expressions. *
* * Maps operator characters to their corresponding AST token types
* \tparam ScannerT Scanner type used by Boost.Spirit for parsing * for addition and subtraction operations.
*/ */
template <typename ScannerT> struct additive_op_ : x3::symbols<ast::optoken>
struct definition {
additive_op_()
{ {
//-------------------------------------------------------------------- add("+", ast::op_plus)("-", ast::op_minus);
/** }
* \brief Constructor that defines all grammar rules. };
*
* Sets up the complete grammar hierarchy for parsing function expressions:
* - Terminals: real numbers, constants (PI, GAMMA_MU, B, EN, T), parameters (PAR), maps (MAP)
* - Functions: trigonometric (cos, sin, tan), hyperbolic (cosh, sinh, tanh),
* inverse trigonometric (acos, asin, atan), inverse hyperbolic (acosh, asinh, atanh),
* logarithmic (log, ln), exponential (exp), square root (sqrt), power (pow)
* - Operators: addition (+), subtraction (-), multiplication (*), division (/)
* - Expressions: Hierarchical structure following operator precedence
*
* \param self Reference to the enclosing PFunctionGrammar (unused but required by Boost.Spirit)
*/
definition(PFunctionGrammar const& /*self*/)
{
// Start grammar definition
real = leaf_node_d[ real_p ];
const_pi = leaf_node_d[ str_p("PI") ]; inline additive_op_ additive_op; ///< Global instance of additive operator symbol table
const_gamma_mu = leaf_node_d[ str_p("GAMMA_MU") ]; /**
* @brief Symbol table for multiplicative operators (* and /).
*
* Maps operator characters to their corresponding AST token types
* for multiplication and division operations.
*/
struct multiplicative_op_ : x3::symbols<ast::optoken>
{
multiplicative_op_()
{
add("*", ast::op_times)("/", ast::op_divide);
}
};
const_field = leaf_node_d[ str_p("B") ] | inline multiplicative_op_ multiplicative_op; ///< Global instance of multiplicative operator symbol table
leaf_node_d[ str_p("-B") ];
const_energy = leaf_node_d[ str_p("EN") ] | /**
leaf_node_d[ str_p("-EN") ]; * @brief Symbol table for mathematical function names.
*
* Maps uppercase function names (COS, SIN, EXP, etc.) to their
* corresponding AST function identifiers. Supports trigonometric,
* hyperbolic, inverse, exponential, and logarithmic functions.
*/
struct fun_tok_ : x3::symbols<ast::funid>
{
fun_tok_()
{
add
("COS", ast::fun_cos)
("SIN", ast::fun_sin)
("TAN", ast::fun_tan)
("COSH", ast::fun_cosh)
("SINH", ast::fun_sinh)
("TANH", ast::fun_tanh)
("ACOS", ast::fun_acos)
("ASIN", ast::fun_asin)
("ATAN", ast::fun_atan)
("ACOSH", ast::fun_acosh)
("ASINH", ast::fun_asinh)
("ATANH", ast::fun_atanh)
("LOG", ast::fun_log)
("LN", ast::fun_ln)
("EXP", ast::fun_exp)
("SQRT", ast::fun_sqrt);
}
};
const_temp = leaf_node_d[ ( lexeme_d[ "T" >> +digit_p ] ) ] | inline fun_tok_ fun_tok; ///< Global instance of function name symbol table
leaf_node_d[ ( lexeme_d[ "-T" >> +digit_p ] ) ];
fun_label = leaf_node_d[ ( lexeme_d[ "FUN" >> +digit_p ] ) ]; /**
* @brief Symbol table for named constants.
*
* Maps constant names (PI, GAMMA_MU) to their corresponding
* AST constant type identifiers.
*/
struct const_tok_ : x3::symbols<ast::constant::type>
{
const_tok_()
{
add("PI", ast::constant::pi)("GAMMA_MU", ast::constant::gamma_mu);
}
};
parameter = leaf_node_d[ ( lexeme_d[ "PAR" >> +digit_p ] ) | inline const_tok_ const_tok; ///< Global instance of constant name symbol table
( lexeme_d[ "-PAR" >> +digit_p ] ) ];
map = leaf_node_d[ ( lexeme_d[ "MAP" >> +digit_p ] ) ]; ///////////////////////////////////////////////////////////////////////////
// Grammar Rules - Forward Declarations
///////////////////////////////////////////////////////////////////////////
function = lexeme_d[ root_node_d[ str_p("COS") ] >> ch_p('(') ] >> expression >> ch_p(')') /// Top-level rule: FUN# = expression
| lexeme_d[ root_node_d[ str_p("SIN") ] >> ch_p('(') ] >> expression >> ch_p(')') x3::rule<class assignment, ast::assignment> const assignment = "assignment";
| lexeme_d[ root_node_d[ str_p("TAN") ] >> ch_p('(') ] >> expression >> ch_p(')')
| lexeme_d[ root_node_d[ str_p("COSH") ] >> ch_p('(') ] >> expression >> ch_p(')')
| lexeme_d[ root_node_d[ str_p("SINH") ] >> ch_p('(') ] >> expression >> ch_p(')')
| lexeme_d[ root_node_d[ str_p("TANH") ] >> ch_p('(') ] >> expression >> ch_p(')')
| lexeme_d[ root_node_d[ str_p("ACOS") ] >> ch_p('(') ] >> expression >> ch_p(')')
| lexeme_d[ root_node_d[ str_p("ASIN") ] >> ch_p('(') ] >> expression >> ch_p(')')
| lexeme_d[ root_node_d[ str_p("ATAN") ] >> ch_p('(') ] >> expression >> ch_p(')')
| lexeme_d[ root_node_d[ str_p("ACOSH") ] >> ch_p('(') ] >> expression >> ch_p(')')
| lexeme_d[ root_node_d[ str_p("ASINH") ] >> ch_p('(') ] >> expression >> ch_p(')')
| lexeme_d[ root_node_d[ str_p("ATANH") ] >> ch_p('(') ] >> expression >> ch_p(')')
| lexeme_d[ root_node_d[ str_p("LOG") ] >> ch_p('(') ] >> expression >> ch_p(')')
| lexeme_d[ root_node_d[ str_p("LN") ] >> ch_p('(') ] >> expression >> ch_p(')')
| lexeme_d[ root_node_d[ str_p("EXP") ] >> ch_p('(') ] >> expression >> ch_p(')')
| lexeme_d[ root_node_d[ str_p("SQRT") ] >> ch_p('(') ] >> expression >> ch_p(')')
;
power = lexeme_d[ root_node_d[ str_p("POW") ] >> ch_p('(') ] >> expression >> ch_p(',') >> expression >> ch_p(')') /// Expression with addition/subtraction (lowest precedence)
; x3::rule<class expression, ast::expression> const expression = "expression";
factor = real /// Term with multiplication/division (higher precedence)
| const_pi x3::rule<class term, ast::expression> const term = "term";
| const_gamma_mu
| const_field
| const_energy
| const_temp
| parameter
| map
| function
| power
| inner_node_d[ch_p('(') >> expression >> ch_p(')')]
;
term = factor >> /// Factor: literal, constant, parameter, function, or parenthesized expression (highest precedence)
*( (root_node_d[ch_p('*')] >> factor) x3::rule<class factor, ast::operand> const factor = "factor";
| (root_node_d[ch_p('/')] >> factor)
);
expression = term >> /// Constant: PI, GAMMA_MU, B, EN, or T#
*( (root_node_d[ch_p('+')] >> term) x3::rule<class constant, ast::constant> const constant = "constant";
| (root_node_d[ch_p('-')] >> term)
);
assignment = (fun_label >> ch_p('=') >> expression); /// Parameter reference: PAR# or -PAR#
// End grammar definition x3::rule<class parameter, ast::parameter> const parameter = "parameter";
// turn on the debugging info. /// Map reference: MAP# or -MAP#
BOOST_SPIRIT_DEBUG_RULE(real); x3::rule<class map_ref, ast::map_ref> const map = "map";
BOOST_SPIRIT_DEBUG_RULE(const_pi);
BOOST_SPIRIT_DEBUG_RULE(const_gamma_mu);
BOOST_SPIRIT_DEBUG_RULE(const_field);
BOOST_SPIRIT_DEBUG_RULE(const_energy);
BOOST_SPIRIT_DEBUG_RULE(const_temp);
BOOST_SPIRIT_DEBUG_RULE(fun_label);
BOOST_SPIRIT_DEBUG_RULE(parameter);
BOOST_SPIRIT_DEBUG_RULE(map);
BOOST_SPIRIT_DEBUG_RULE(function);
BOOST_SPIRIT_DEBUG_RULE(power);
BOOST_SPIRIT_DEBUG_RULE(factor);
BOOST_SPIRIT_DEBUG_RULE(term);
BOOST_SPIRIT_DEBUG_RULE(expression);
BOOST_SPIRIT_DEBUG_RULE(assignment);
}
rule<ScannerT, parser_context<>, parser_tag<assignmentID> > assignment; ///< Rule for assignment: FUN# = expression /// Function call: FUNC(expression)
rule<ScannerT, parser_context<>, parser_tag<expressionID> > expression; ///< Rule for expression: term { ('+' | '-') term } x3::rule<class function_call, ast::function_call> const function = "function";
rule<ScannerT, parser_context<>, parser_tag<termID> > term; ///< Rule for term: factor { ('*' | '/') factor }
rule<ScannerT, parser_context<>, parser_tag<factorID> > factor; ///< Rule for factor: operand or parenthesized expression
rule<ScannerT, parser_context<>, parser_tag<functionID> > function; ///< Rule for mathematical functions
rule<ScannerT, parser_context<>, parser_tag<powerID> > power; ///< Rule for power function POW(base, exponent)
rule<ScannerT, parser_context<>, parser_tag<mapID> > map; ///< Rule for map reference MAP#
rule<ScannerT, parser_context<>, parser_tag<parameterID> > parameter; ///< Rule for parameter reference PAR# or -PAR#
rule<ScannerT, parser_context<>, parser_tag<funLabelID> > fun_label; ///< Rule for function label FUN#
rule<ScannerT, parser_context<>, parser_tag<constTempID> > const_temp; ///< Rule for temperature constant T# or -T#
rule<ScannerT, parser_context<>, parser_tag<constEnergyID> > const_energy; ///< Rule for energy constant EN or -EN
rule<ScannerT, parser_context<>, parser_tag<constFieldID> > const_field; ///< Rule for field constant B or -B
rule<ScannerT, parser_context<>, parser_tag<constGammaMuID> > const_gamma_mu;///< Rule for muon gyromagnetic ratio constant
rule<ScannerT, parser_context<>, parser_tag<constPiID> > const_pi; ///< Rule for PI constant
rule<ScannerT, parser_context<>, parser_tag<realID> > real; ///< Rule for real number literals
//-------------------------------------------------------------------- /// Power operation: POW(base, exponent)
/** x3::rule<class power_call, ast::power_call> const power = "power";
* \brief Returns the starting rule for the grammar.
* ///////////////////////////////////////////////////////////////////////////
* The parser begins by matching the assignment rule, which represents // Grammar Rule Definitions
* a complete function definition (e.g., FUN1 = PAR1 * cos(PAR2 * PI)). ///////////////////////////////////////////////////////////////////////////
*
* \return Reference to the assignment rule as the grammar entry point /**
*/ * @brief Assignment rule: FUN# = expression
rule<ScannerT, parser_context<>, parser_tag<assignmentID> > const& *
start() const { return assignment; } * Parses a function assignment statement, extracting the function number
}; * and the expression to evaluate.
}; */
auto const assignment_def =
lit("FUN") >> int_ >> '=' >> expression;
/**
* @brief Expression rule: term ((+|-) term)*
*
* Handles addition and subtraction with left-associative evaluation.
* Lower precedence than multiplication/division.
*/
auto const expression_def =
term >> *(additive_op >> term);
/**
* @brief Term rule: factor ((*|/) factor)*
*
* Handles multiplication and division with left-associative evaluation.
* Higher precedence than addition/subtraction.
*/
auto const term_def =
factor >> *(multiplicative_op >> factor);
/**
* @brief Factor rule: the atomic elements of expressions.
*
* Matches numeric literals, constants, parameters, maps, function calls,
* power operations, or parenthesized sub-expressions. Parentheses allow
* overriding operator precedence.
*/
auto const factor_def =
double_
| constant
| parameter
| map
| function
| power
| ('(' >> expression >> ')');
/**
* @brief Constant rule: PI | GAMMA_MU | B | -B | EN | -EN | T# | -T#
*
* Parses symbolic constants, optionally with negation. Temperature
* constants include an index number (T0, T1, T2, etc.).
*/
auto const constant_def =
(const_tok >> attr(false) >> attr(0))
| (lit("B") >> attr(ast::constant::field) >> attr(false) >> attr(0))
| (lit("-B") >> attr(ast::constant::field) >> attr(true) >> attr(0))
| (lit("EN") >> attr(ast::constant::energy) >> attr(false) >> attr(0))
| (lit("-EN") >> attr(ast::constant::energy) >> attr(true) >> attr(0))
| (lit('T') >> attr(ast::constant::temp) >> attr(false) >> int_)
| (lit("-T") >> attr(ast::constant::temp) >> attr(true) >> int_);
/**
* @brief Parameter rule: PAR# | -PAR#
*
* Parses parameter references with 1-based indexing. The lexeme directive
* for -PAR# ensures the minus sign is treated as part of the token,
* not as a separate operator.
*/
auto const parameter_def =
(lexeme[lit("-PAR") >> int_] >> attr(true))
| (lit("PAR") >> int_ >> attr(false));
/**
* @brief Map rule: MAP# | -MAP#
*
* Parses map references for indirect parameter lookup with 1-based indexing.
* The lexeme directive for -MAP# ensures the minus sign is part of the token.
*/
auto const map_def =
(lexeme[lit("-MAP") >> int_] >> attr(true))
| (lit("MAP") >> int_ >> attr(false));
/**
* @brief Function rule: FUNC(expression)
*
* Parses mathematical function calls. The function name is matched by
* fun_tok and the argument is a full expression.
*/
auto const function_def =
fun_tok >> '(' >> expression >> ')';
/**
* @brief Power rule: POW(base, exponent)
*
* Parses power operations with two expression arguments separated by comma.
* Both base and exponent can be arbitrary expressions.
*/
auto const power_def =
lit("POW") >> '(' >> expression >> ',' >> expression >> ')';
/**
* @brief Links rule names to their definitions.
*
* Required by Boost.Spirit X3 to connect the forward-declared rules
* with their actual parsing logic.
*/
BOOST_SPIRIT_DEFINE(
assignment,
expression,
term,
factor,
constant,
parameter,
map,
function,
power
)
}}
/**
* @namespace musrfit
* @brief Top-level namespace for musrfit components.
*/
namespace musrfit {
/**
* @brief Provides access to the top-level grammar rule.
*
* Returns a reference to the assignment rule, which is the entry point
* for parsing complete function assignment statements (FUN# = expression).
* Use this function to obtain the grammar for parsing with Spirit X3.
*
* @return Constant reference to the assignment grammar rule
*
* @code
* auto const& grammar = musrfit::function_grammar();
* bool success = x3::phrase_parse(iter, end, grammar, x3::space, result);
* @endcode
*/
inline auto const& function_grammar()
{
return grammar::assignment;
}
}
#endif // _PFUNCTIONGRAMMAR_H_ #endif // _PFUNCTIONGRAMMAR_H_

View File

@@ -51,7 +51,7 @@ namespace mupp
* handler to pinpoint the exact location of errors in the source text. * handler to pinpoint the exact location of errors in the source text.
* *
* The annotation is performed after successful parsing but before semantic * 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) * @tparam Iterator the iterator type for traversing the source code (typically std::string::const_iterator)
*/ */
@@ -59,9 +59,6 @@ namespace mupp
template <typename Iterator> template <typename Iterator>
struct PAnnotation 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 std::vector<Iterator>& iters; ///< Reference to vector storing iterator positions indexed by AST node IDs
@@ -79,8 +76,6 @@ namespace mupp
*/ */
struct set_id struct set_id
{ {
typedef void result_type; ///< Return type for Boost.Phoenix compatibility
int id; ///< The ID to assign to tagged AST nodes 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 * This error handler logs detailed error information to a file, including
* the error message, line number, source line, and a visual pointer to the * 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 * mechanism and works in conjunction with the PAnnotation handler to map
* AST node IDs back to source positions. * AST node IDs back to source positions.
* *
@@ -58,9 +58,6 @@ namespace mupp
template <typename Iterator> template <typename Iterator>
struct PErrorHandler 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. * @brief Constructor that stores the source code range.

View File

@@ -33,21 +33,12 @@
#ifndef _PEXPRESSION_HPP_ #ifndef _PEXPRESSION_HPP_
#define _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 // 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 "PAst.hpp"
#include "PErrorHandler.hpp" #include "PErrorHandler.hpp"
#include "PSkipper.hpp" #include "PSkipper.hpp"
@@ -55,14 +46,14 @@
namespace mupp { namespace parser namespace mupp { namespace parser
{ {
namespace qi = boost::spirit::qi; namespace x3 = boost::spirit::x3;
namespace ascii = boost::spirit::ascii; namespace ascii = boost::spirit::x3::ascii;
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
/** /**
* @brief The PExpression grammar for parsing mathematical expressions. * @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: * expressions with proper operator precedence and associativity:
* - Primary expressions: numbers, variables, functions, parenthesized expressions * - Primary expressions: numbers, variables, functions, parenthesized expressions
* - Unary operators: +, - (highest precedence) * - Unary operators: +, - (highest precedence)
@@ -80,31 +71,27 @@ namespace mupp { namespace parser
* - 3.14 * $radius * - 3.14 * $radius
* - sin($theta) + cos($phi) * - sin($theta) + cos($phi)
* - pow($x, 2.0) + pow($y, 2.0) * - 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 (+, -) // Rule IDs
qi::symbols<char, ast::optoken> multiplicative_op; ///< Symbol table for multiplicative operators (*, /) struct expr_class;
qi::symbols<char, ast::optoken> unary_op; ///< Symbol table for unary operators (+, -) struct additive_expr_class;
qi::symbols<char, ast::funid> fun_tok; ///< Symbol table for function names 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 // Rule declarations
qi::rule<Iterator, ast::expression(), PSkipper<Iterator> > additive_expr; ///< Additive expression rule (lowest precedence) typedef x3::rule<expr_class, ast::expression> expr_type;
qi::rule<Iterator, ast::expression(), PSkipper<Iterator> > multiplicative_expr; ///< Multiplicative expression rule (medium precedence) typedef x3::rule<additive_expr_class, ast::expression> additive_expr_type;
qi::rule<Iterator, ast::operand(), PSkipper<Iterator> > unary_expr; ///< Unary expression rule typedef x3::rule<multiplicative_expr_class, ast::expression> multiplicative_expr_type;
qi::rule<Iterator, ast::operand(), PSkipper<Iterator> > primary_expr; ///< Primary expression rule (highest precedence) typedef x3::rule<unary_expr_class, ast::operand> unary_expr_type;
qi::rule<Iterator, std::string(), PSkipper<Iterator> > identifier; ///< Identifier rule (variables prefixed with '$') 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_ #endif // _PEXPRESSION_HPP_

View File

@@ -9,8 +9,8 @@
see https://github.com/boostorg/spirit see https://github.com/boostorg/spirit
This file contains the implementation (definition) of the PExpression This file contains the implementation (definition) of the PExpression
template grammar. It defines the actual grammar rules and their semantic grammar. It defines the actual grammar rules and their semantic
actions using Boost.Spirit Qi. actions using Boost.Spirit X3.
The grammar implements expression parsing with proper operator precedence: The grammar implements expression parsing with proper operator precedence:
- Primary expressions (literals, variables, functions) - Primary expressions (literals, variables, functions)
@@ -46,93 +46,116 @@
#include "PExpression.hpp" #include "PExpression.hpp"
#include "PErrorHandler.hpp" #include "PErrorHandler.hpp"
#include "PAnnotation.hpp" #include "PAnnotation.hpp"
#include <boost/phoenix/function.hpp>
namespace mupp { namespace parser namespace mupp { namespace parser
{ {
template <typename Iterator> namespace x3 = boost::spirit::x3;
PExpression<Iterator>::PExpression(PErrorHandler<Iterator>& error_handler) namespace ascii = boost::spirit::x3::ascii;
: PExpression::base_type(expr)
{
qi::_1_type _1;
qi::_2_type _2;
qi::_3_type _3;
qi::_4_type _4;
qi::char_type char_; using x3::char_;
qi::double_type double_; using x3::double_;
qi::_val_type _val; using x3::raw;
qi::raw_type raw; using x3::lexeme;
qi::lexeme_type lexeme; using ascii::alpha;
qi::alpha_type alpha; using ascii::alnum;
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;
/////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////
// Tokens // Symbol tables for operators and functions
additive_op.add ///////////////////////////////////////////////////////////////////////
("+", ast::op_plus)
("-", ast::op_minus)
;
multiplicative_op.add inline struct additive_op_ : x3::symbols<ast::optoken>
("*", ast::op_times) {
("/", ast::op_divide) additive_op_()
; {
add
("+", ast::op_plus)
("-", ast::op_minus)
;
}
} additive_op;
unary_op.add inline struct multiplicative_op_ : x3::symbols<ast::optoken>
("+", ast::op_positive) {
("-", ast::op_negative) multiplicative_op_()
; {
add
("*", ast::op_times)
("/", ast::op_divide)
;
}
} multiplicative_op;
fun_tok.add inline struct unary_op_ : x3::symbols<ast::optoken>
("max", ast::fun_max) {
("min", ast::fun_min) unary_op_()
("abs", ast::fun_abs) {
("sin", ast::fun_sin) add
("cos", ast::fun_cos) ("+", ast::op_positive)
("tan", ast::fun_tan) ("-", ast::op_negative)
("sinh", ast::fun_sinh) ;
("cosh", ast::fun_cosh) }
("tanh", ast::fun_tanh) } unary_op;
("asin", ast::fun_asin)
("acos", ast::fun_acos) inline struct fun_tok_ : x3::symbols<ast::funid>
("atan", ast::fun_atan) {
("exp", ast::fun_exp) fun_tok_()
("log", ast::fun_log) {
("ln", ast::fun_ln) add
("sqrt", ast::fun_sqrt) ("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 // Grammar
expr = ///////////////////////////////////////////////////////////////////////
additive_expr.alias()
inline auto const expr_def =
additive_expr
; ;
additive_expr = inline auto const additive_expr_def =
multiplicative_expr multiplicative_expr
>> *(additive_op > multiplicative_expr) >> *(additive_op > multiplicative_expr)
; ;
multiplicative_expr = inline auto const multiplicative_expr_def =
unary_expr unary_expr
>> *(multiplicative_op > unary_expr) >> *(multiplicative_op > unary_expr)
; ;
unary_expr = inline auto const unary_expr_def =
primary_expr primary_expr
| (unary_op > primary_expr) | (unary_op > primary_expr)
; ;
primary_expr = inline auto const primary_expr_def =
double_ double_
| identifier | identifier
| fun_tok > '(' > expr > ')' | fun_tok > '(' > expr > ')'
@@ -140,36 +163,13 @@ namespace mupp { namespace parser
| '(' > expr > ')' | '(' > expr > ')'
; ;
identifier = inline auto const identifier_def =
raw[lexeme['$' >> *(alnum | '_')]] raw[lexeme['$' >> *(alnum | '_')]]
; ;
// name all the rules BOOST_SPIRIT_DEFINE(expr, additive_expr, multiplicative_expr,
additive_expr.name("additive_expr"); unary_expr, primary_expr, identifier)
multiplicative_expr.name("multiplicative_expr");
unary_expr.name("unary_expr");
primary_expr.name("primary_expr");
identifier.name("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_ #ifndef _PSKIPPER_HPP_
#define _PSKIPPER_HPP_ #define _PSKIPPER_HPP_
#include <boost/spirit/include/qi.hpp> #include <boost/spirit/home/x3.hpp>
namespace mupp { namespace parser namespace mupp { namespace parser
{ {
namespace qi = boost::spirit::qi; namespace x3 = boost::spirit::x3;
namespace ascii = boost::spirit::ascii; namespace ascii = boost::spirit::x3::ascii;
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
/** /**
@@ -61,38 +61,22 @@ namespace mupp { namespace parser
* / * This is a * / * This is a
* multi-line comment * / * multi-line comment * /
* @endcode * @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 = inline auto const single_line_comment = (lit('%') | lit('#') | lit("//")) >> *print >> eol;
space // tab/space/cr/lf
| "/*" >> *(char_ - "*/") >> "*/" // C-style comments
| single_line_comment
;
}
qi::rule<Iterator> single_line_comment; ///< Rule for single-line comments (%, #, //) inline auto const skipper =
qi::rule<Iterator> start; ///< Top-level skipper rule space // tab/space/cr/lf
}; | "/*" >> *(char_ - "*/") >> "*/" // C-style comments
| single_line_comment
;
}} }}
#endif // _PSKIPPER_HPP_ #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 * The grammar parses a statement list (one or more statements) and builds
* an AST that can be analyzed and evaluated by the semantic analyzer. * 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 // Rule IDs
qi::rule<Iterator, ast::statement_list(), PSkipper<Iterator> > statement_list; ///< Rule for parsing a list of statements struct statement_list_class;
qi::rule<Iterator, ast::variable_declaration(), PSkipper<Iterator> > variable_declaration; ///< Rule for variable declarations struct variable_declaration_class;
qi::rule<Iterator, ast::assignment(), PSkipper<Iterator> > assignment; ///< Rule for assignment statements struct assignment_class;
qi::rule<Iterator, std::string(), PSkipper<Iterator> > identifier; ///< Rule for identifiers (without '$' prefix) 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_ #endif // _PSTATEMENT_HPP_

View File

@@ -9,8 +9,8 @@
see https://github.com/boostorg/spirit see https://github.com/boostorg/spirit
This file contains the implementation (definition) of the PStatement This file contains the implementation (definition) of the PStatement
template grammar. It defines the grammar rules for parsing variable grammar. It defines the grammar rules for parsing variable
declarations and assignments using Boost.Spirit Qi. declarations and assignments using Boost.Spirit X3.
The grammar supports: The grammar supports:
- Variable declarations: var <identifier> = <expression> - Variable declarations: var <identifier> = <expression>
@@ -43,77 +43,55 @@
***************************************************************************/ ***************************************************************************/
#include "PStatement.hpp" #include "PStatement.hpp"
#include "PExpressionDef.hpp"
#include "PErrorHandler.hpp" #include "PErrorHandler.hpp"
#include "PAnnotation.hpp" #include "PAnnotation.hpp"
namespace mupp { namespace parser namespace mupp { namespace parser
{ {
template <typename Iterator> namespace x3 = boost::spirit::x3;
PStatement<Iterator>::PStatement(PErrorHandler<Iterator>& error_handler) namespace ascii = boost::spirit::x3::ascii;
: PStatement::base_type(statement_list), expr(error_handler)
{
qi::_1_type _1;
qi::_2_type _2;
qi::_3_type _3;
qi::_4_type _4;
qi::_val_type _val; using x3::raw;
qi::raw_type raw; using x3::lexeme;
qi::lexeme_type lexeme; using ascii::alpha;
qi::alpha_type alpha; using ascii::alnum;
qi::alnum_type alnum;
using qi::on_error; ///////////////////////////////////////////////////////////////////////
using qi::on_success; // Rule definitions
using qi::fail; ///////////////////////////////////////////////////////////////////////
using boost::phoenix::function;
typedef function<mupp::PErrorHandler<Iterator> > error_handler_function; inline statement_list_type const statement_list = "statement_list";
typedef function<mupp::PAnnotation<Iterator> > annotation_function; 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) +(variable_declaration | assignment)
; ;
identifier = inline auto const stmt_identifier_def =
raw[lexeme[(alpha | '_') >> *(alnum | '_')]] raw[lexeme[(alpha | '_') >> *(alnum | '_')]]
; ;
variable_declaration = inline auto const variable_declaration_def =
lexeme["var" >> !(alnum | '_')] // make sure we have whole words lexeme["var" >> !(alnum | '_')] // make sure we have whole words
> identifier > stmt_identifier
> -('=' > expr) > -('=' > expr)
; ;
assignment = inline auto const assignment_def =
identifier stmt_identifier
> '=' > '='
> expr > expr
; ;
// name all the rules BOOST_SPIRIT_DEFINE(statement_list, variable_declaration, assignment, stmt_identifier)
statement_list.name("statement_list");
identifier.name("identifier");
variable_declaration.name("variable_declaration");
assignment.name("assignment");
// 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, Based on Joel de Guzman example on calc7,
see https://github.com/boostorg/spirit see https://github.com/boostorg/spirit
This file explicitly instantiates the PExpression template grammar for This file instantiates the PExpression grammar rules for
std::string::const_iterator. Template instantiation in a separate std::string::const_iterator. Instantiation in a separate
compilation unit reduces compile times and allows the grammar compilation unit reduces compile times and allows the grammar
implementation to be hidden from clients. implementation to be hidden from clients.
@@ -35,11 +35,4 @@
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/ ***************************************************************************/
#if defined(_MSC_VER)
# pragma warning(disable: 4345)
#endif
#include "PExpressionDef.hpp" #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, Based on Joel de Guzman example on calc7,
see https://github.com/boostorg/spirit see https://github.com/boostorg/spirit
This file explicitly instantiates the PStatement template grammar for This file instantiates the PStatement grammar rules for
std::string::const_iterator. Template instantiation in a separate std::string::const_iterator. Instantiation in a separate
compilation unit reduces compile times and allows the grammar compilation unit reduces compile times and allows the grammar
implementation to be hidden from clients. implementation to be hidden from clients.
@@ -35,11 +35,4 @@
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/ ***************************************************************************/
#if defined(_MSC_VER)
# pragma warning(disable: 4345)
#endif
#include "PStatementDef.hpp" #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 "PSkipper.hpp"
#include "PErrorHandler.hpp" #include "PErrorHandler.hpp"
#include "PStatement.hpp" #include "PStatement.hpp"
#include "PStatementDef.hpp"
#include "PProgram.hpp" #include "PProgram.hpp"
#include <boost/spirit/home/x3.hpp>
//-------------------------------------------------------------------------- //--------------------------------------------------------------------------
/** /**
* @brief Default constructor creating an invalid handler. * @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(); iterator_type end = fParseStr.end();
mupp::PErrorHandler<iterator_type> error_handler(iter, end); // the error handler 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::prog::PProgram prog(error_handler); // our compiler, and exec
mupp::parser::PSkipper<iterator_type> skipper; // the skipper parser
// perform the parsing // 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 (success && iter == end) {
if (prog(fAst)) { // semantic analysis if (prog(fAst)) { // semantic analysis
std::vector<double> data, dataErr; std::vector<double> data, dataErr;

View File

@@ -158,11 +158,11 @@ double PFunction::Eval()
//========================================================================== //==========================================================================
/** /**
* @brief Evaluates a nil (empty) AST node. * @brief Evaluates a leer (empty) AST node.
* *
* @return Always returns 0.0 * @return Always returns 0.0
*/ */
double PFunction::EvalVisitor::operator()(const musrfit::ast::nil&) const double PFunction::EvalVisitor::operator()(const musrfit::ast::leer &) const
{ {
return 0.0; return 0.0;
} }

View File

@@ -129,11 +129,11 @@ class PFunction {
: fMap(map), fParam(param) {} : fMap(map), fParam(param) {}
/** /**
* @brief Evaluates a nil/empty AST node. * @brief Evaluates a leer/empty AST node.
* @param AST nil node (unused) * @param AST leer node (unused)
* @return 0.0 * @return 0.0
*/ */
double operator()(const musrfit::ast::nil&) const; double operator()(const musrfit::ast::leer&) const;
/** /**
* @brief Evaluates a numeric literal. * @brief Evaluates a numeric literal.

View File

@@ -107,7 +107,7 @@ namespace musrfit { namespace ast
* *
* Used as a placeholder in variant types where no value is present. * Used as a placeholder in variant types where no value is present.
*/ */
struct nil {}; struct leer {};
// Forward declarations for recursive AST structures // Forward declarations for recursive AST structures
struct expression; struct expression;
@@ -173,7 +173,7 @@ namespace musrfit { namespace ast
* recursive grammar structures. * recursive grammar structures.
*/ */
typedef boost::variant< typedef boost::variant<
nil ///< Empty placeholder leer ///< Empty placeholder
, double ///< Numeric literal , double ///< Numeric literal
, constant ///< Constant value , constant ///< Constant value
, parameter ///< Parameter reference , parameter ///< Parameter reference
@@ -246,11 +246,11 @@ namespace musrfit { namespace ast
}; };
/** /**
* @brief Stream output operator for nil nodes (debugging support). * @brief Stream output operator for leer nodes (debugging support).
* @param out the output stream * @param out the output stream
* @return the output stream for chaining * @return the output stream for chaining
*/ */
inline std::ostream& operator<<(std::ostream& out, nil) { out << std::string("nil"); return out; } inline std::ostream& operator<<(std::ostream& out, leer) { out << std::string("leer"); return out; }
}} }}
// Boost.Fusion adaptations to make structures compatible with X3 attribute propagation // Boost.Fusion adaptations to make structures compatible with X3 attribute propagation