modernize musrfit function handling using spirit x3 instead of spirit classic now.

This commit is contained in:
2025-12-30 13:31:18 +01:00
parent 046ab98144
commit 4cd4b7878f
4 changed files with 893 additions and 1062 deletions

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();
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 fFuncs.push_back(func); // feeds it to the functions vector
} else { } else {
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;
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 << "**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 /**
* \brief Visitor class for evaluating the AST.
*
* 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.
*/
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::nil&) const;
Double_t operator()(double val) const;
Double_t operator()(const musrfit::ast::constant& c) const;
Double_t operator()(const musrfit::ast::parameter& p) const;
Double_t operator()(const musrfit::ast::map_ref& m) const;
Double_t operator()(const musrfit::ast::function_call& f) const;
Double_t operator()(const musrfit::ast::power_call& p) const;
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<Double_t> fParam; ///< Current fit parameter values for evaluation
std::vector<Int_t> fMap; ///< Map vector for indirect parameter references 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 Bool_t fValid; ///< Validity flag: true if function parsed and initialized successfully
Int_t fFuncNo; ///< Function number extracted from label (x in FUNx) Int_t fFuncNo; ///< Function number extracted from label (x in FUNx)
//------------------------------------------------------------------------
/**
* \brief Initiates generation of human-readable function string.
*
* \param info AST parse tree to convert to string
*/
virtual void EvalTreeForString(tree_parse_info<> info);
//------------------------------------------------------------------------
/**
* \brief Recursively generates formatted function string from AST.
*
* \param i Iterator pointing to current AST node
* \param funcFlag Flag indicating if currently inside a function call
*/
virtual void EvalTreeForStringExpression(iter_t const& i, bool funcFlag=false);
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.)
}; };

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#) // Symbol tables - using inline to avoid multiple definition errors
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
//------------------------------------------------------------------------
/** /**
* \brief Inner template structure defining the grammar rules. * @brief Symbol table for additive operators (+ and -).
* *
* This template structure contains the actual grammar rule definitions * Maps operator characters to their corresponding AST token types
* using Boost.Spirit syntax. It defines how the parser should recognize * for addition and subtraction operations.
* and build an abstract syntax tree (AST) for function expressions.
*
* \tparam ScannerT Scanner type used by Boost.Spirit for parsing
*/ */
template <typename ScannerT> struct additive_op_ : x3::symbols<ast::optoken>
struct definition
{ {
//-------------------------------------------------------------------- additive_op_()
{
add("+", ast::op_plus)("-", ast::op_minus);
}
};
inline additive_op_ additive_op; ///< Global instance of additive operator symbol table
/** /**
* \brief Constructor that defines all grammar rules. * @brief Symbol table for multiplicative operators (* and /).
* *
* Sets up the complete grammar hierarchy for parsing function expressions: * Maps operator characters to their corresponding AST token types
* - Terminals: real numbers, constants (PI, GAMMA_MU, B, EN, T), parameters (PAR), maps (MAP) * for multiplication and division operations.
* - 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*/) struct multiplicative_op_ : x3::symbols<ast::optoken>
{ {
// Start grammar definition multiplicative_op_()
real = leaf_node_d[ real_p ]; {
add("*", ast::op_times)("/", ast::op_divide);
}
};
const_pi = leaf_node_d[ str_p("PI") ]; inline multiplicative_op_ multiplicative_op; ///< Global instance of multiplicative operator symbol table
const_gamma_mu = leaf_node_d[ str_p("GAMMA_MU") ]; /**
* @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_field = leaf_node_d[ str_p("B") ] | inline fun_tok_ fun_tok; ///< Global instance of function name 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 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);
}
};
const_temp = leaf_node_d[ ( lexeme_d[ "T" >> +digit_p ] ) ] | inline const_tok_ const_tok; ///< Global instance of constant name symbol table
leaf_node_d[ ( lexeme_d[ "-T" >> +digit_p ] ) ];
fun_label = leaf_node_d[ ( lexeme_d[ "FUN" >> +digit_p ] ) ]; ///////////////////////////////////////////////////////////////////////////
// Grammar Rules - Forward Declarations
///////////////////////////////////////////////////////////////////////////
parameter = leaf_node_d[ ( lexeme_d[ "PAR" >> +digit_p ] ) | /// Top-level rule: FUN# = expression
( lexeme_d[ "-PAR" >> +digit_p ] ) ]; x3::rule<class assignment, ast::assignment> const assignment = "assignment";
map = leaf_node_d[ ( lexeme_d[ "MAP" >> +digit_p ] ) ]; /// Expression with addition/subtraction (lowest precedence)
x3::rule<class expression, ast::expression> const expression = "expression";
function = lexeme_d[ root_node_d[ str_p("COS") ] >> ch_p('(') ] >> expression >> ch_p(')') /// Term with multiplication/division (higher precedence)
| lexeme_d[ root_node_d[ str_p("SIN") ] >> ch_p('(') ] >> expression >> ch_p(')') x3::rule<class term, ast::expression> const term = "term";
| 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(')') /// Factor: literal, constant, parameter, function, or parenthesized expression (highest precedence)
; x3::rule<class factor, ast::operand> const factor = "factor";
factor = real /// Constant: PI, GAMMA_MU, B, EN, or T#
| const_pi x3::rule<class constant, ast::constant> const constant = "constant";
| const_gamma_mu
| const_field /// Parameter reference: PAR# or -PAR#
| const_energy x3::rule<class parameter, ast::parameter> const parameter = "parameter";
| const_temp
/// Map reference: MAP# or -MAP#
x3::rule<class map_ref, ast::map_ref> const map = "map";
/// Function call: FUNC(expression)
x3::rule<class function_call, ast::function_call> const function = "function";
/// Power operation: POW(base, exponent)
x3::rule<class power_call, ast::power_call> const power = "power";
///////////////////////////////////////////////////////////////////////////
// Grammar Rule Definitions
///////////////////////////////////////////////////////////////////////////
/**
* @brief Assignment rule: FUN# = expression
*
* 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 | parameter
| map | map
| function | function
| power | power
| inner_node_d[ch_p('(') >> expression >> ch_p(')')] | ('(' >> expression >> ')');
;
term = factor >> /**
*( (root_node_d[ch_p('*')] >> factor) * @brief Constant rule: PI | GAMMA_MU | B | -B | EN | -EN | T# | -T#
| (root_node_d[ch_p('/')] >> factor) *
); * 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_);
expression = term >> /**
*( (root_node_d[ch_p('+')] >> term) * @brief Parameter rule: PAR# | -PAR#
| (root_node_d[ch_p('-')] >> term) *
); * 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));
assignment = (fun_label >> ch_p('=') >> expression); /**
// End grammar definition * @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));
// turn on the debugging info. /**
BOOST_SPIRIT_DEBUG_RULE(real); * @brief Function rule: FUNC(expression)
BOOST_SPIRIT_DEBUG_RULE(const_pi); *
BOOST_SPIRIT_DEBUG_RULE(const_gamma_mu); * Parses mathematical function calls. The function name is matched by
BOOST_SPIRIT_DEBUG_RULE(const_field); * fun_tok and the argument is a full expression.
BOOST_SPIRIT_DEBUG_RULE(const_energy); */
BOOST_SPIRIT_DEBUG_RULE(const_temp); auto const function_def =
BOOST_SPIRIT_DEBUG_RULE(fun_label); fun_tok >> '(' >> expression >> ')';
BOOST_SPIRIT_DEBUG_RULE(parameter);
BOOST_SPIRIT_DEBUG_RULE(map); /**
BOOST_SPIRIT_DEBUG_RULE(function); * @brief Power rule: POW(base, exponent)
BOOST_SPIRIT_DEBUG_RULE(power); *
BOOST_SPIRIT_DEBUG_RULE(factor); * Parses power operations with two expression arguments separated by comma.
BOOST_SPIRIT_DEBUG_RULE(term); * Both base and exponent can be arbitrary expressions.
BOOST_SPIRIT_DEBUG_RULE(expression); */
BOOST_SPIRIT_DEBUG_RULE(assignment); 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;
}
} }
rule<ScannerT, parser_context<>, parser_tag<assignmentID> > assignment; ///< Rule for assignment: FUN# = expression
rule<ScannerT, parser_context<>, parser_tag<expressionID> > expression; ///< Rule for expression: term { ('+' | '-') term }
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
//--------------------------------------------------------------------
/**
* \brief Returns the starting rule for the grammar.
*
* The parser begins by matching the assignment rule, which represents
* a complete function definition (e.g., FUN1 = PAR1 * cos(PAR2 * PI)).
*
* \return Reference to the assignment rule as the grammar entry point
*/
rule<ScannerT, parser_context<>, parser_tag<assignmentID> > const&
start() const { return assignment; }
};
};
#endif // _PFUNCTIONGRAMMAR_H_ #endif // _PFUNCTIONGRAMMAR_H_