modernize musrfit function handling using spirit x3 instead of spirit classic now.
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -30,8 +30,12 @@
|
||||
#include <string>
|
||||
#include <cassert>
|
||||
|
||||
#include <boost/spirit/home/x3.hpp>
|
||||
|
||||
#include "PFunctionHandler.h"
|
||||
|
||||
namespace x3 = boost::spirit::x3;
|
||||
|
||||
//-------------------------------------------------------------
|
||||
// Constructor
|
||||
//-------------------------------------------------------------
|
||||
@@ -67,7 +71,6 @@ PFunctionHandler::~PFunctionHandler()
|
||||
Bool_t PFunctionHandler::DoParse()
|
||||
{
|
||||
Bool_t success = true;
|
||||
PFunctionGrammar function;
|
||||
TString line;
|
||||
|
||||
// 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();
|
||||
|
||||
// do parsing
|
||||
tree_parse_info<> info = ast_parse(line.Data(), function, space_p);
|
||||
// do parsing with X3
|
||||
musrfit::ast::assignment assignment;
|
||||
std::string str(line.Data());
|
||||
auto iter = str.begin();
|
||||
auto end = str.end();
|
||||
|
||||
if (info.full) { // parsing successful
|
||||
PFunction func(info); // generate an evaluation function object based on the AST tree
|
||||
fFuncs.push_back(func); // feeds it to the functions vector
|
||||
} else {
|
||||
// Get the X3 grammar
|
||||
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
|
||||
} 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 << "**ERROR**: Expected: " << e.which() << std::endl;
|
||||
success = false;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
***************************************************************************/
|
||||
|
||||
/***************************************************************************
|
||||
* Copyright (C) 2007-2025 by Andreas Suter *
|
||||
* Copyright (C) 2007-2026 by Andreas Suter *
|
||||
* andreas.suter@psi.ch *
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
@@ -33,85 +33,23 @@
|
||||
#include <vector>
|
||||
|
||||
#include <boost/version.hpp>
|
||||
|
||||
#if BOOST_VERSION >= 103800
|
||||
# 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 <boost/variant/static_visitor.hpp>
|
||||
#include <boost/variant/apply_visitor.hpp>
|
||||
|
||||
#include <TString.h>
|
||||
|
||||
#include "PMusr.h"
|
||||
#include "PFunctionAst.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.
|
||||
*
|
||||
* This class handles the complete lifecycle of a function definition:
|
||||
* 1. Parses the function string using PFunctionGrammar into an AST
|
||||
* 2. Converts the AST into an efficient evaluation tree
|
||||
* 3. Validates parameter and map references
|
||||
* 4. Evaluates the function with given parameters and metadata
|
||||
* 2. Validates parameter and map references
|
||||
* 3. Evaluates the function with given parameters and metadata using the visitor pattern
|
||||
*
|
||||
* Functions can reference:
|
||||
* - Fit parameters (PAR1, PAR2, ...)
|
||||
@@ -126,7 +64,7 @@ typedef struct func_tree_node {
|
||||
* \endcode
|
||||
*
|
||||
* \see PFunctionGrammar for the grammar definition
|
||||
* \see PFuncTreeNode for the evaluation tree structure
|
||||
* \see musrfit::ast for the AST structure
|
||||
*/
|
||||
class PFunction {
|
||||
public:
|
||||
@@ -134,13 +72,13 @@ class PFunction {
|
||||
/**
|
||||
* \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();
|
||||
|
||||
@@ -202,101 +140,72 @@ class PFunction {
|
||||
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
|
||||
*/
|
||||
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 operand Current operand being checked
|
||||
* \param mapSize Number of available map entries
|
||||
* \param paramSize Number of available fit parameters
|
||||
* \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 node Evaluation tree node to fill
|
||||
* \param operand Operand AST to convert to string
|
||||
* \return Formatted string representation
|
||||
*/
|
||||
virtual void FillFuncEvalTree(iter_t const& i, PFuncTreeNode &node);
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
/**
|
||||
* \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);
|
||||
virtual TString GenerateStringOperand(const musrfit::ast::operand& operand);
|
||||
|
||||
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) {}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
/**
|
||||
* \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);
|
||||
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<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
|
||||
|
||||
PMetaData fMetaData; ///< Metadata from experimental data (field, energy, temperature, etc.)
|
||||
};
|
||||
|
||||
|
||||
@@ -1,14 +1,17 @@
|
||||
/***************************************************************************
|
||||
|
||||
PFunctionGrammer.h
|
||||
PFunctionGrammar.h
|
||||
|
||||
Author: Andreas Suter
|
||||
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 *
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
@@ -30,223 +33,296 @@
|
||||
#ifndef _PFUNCTIONGRAMMAR_H_
|
||||
#define _PFUNCTIONGRAMMAR_H_
|
||||
|
||||
//#define BOOST_SPIRIT_DEBUG
|
||||
|
||||
// Check Boost version - require 1.61+ for Spirit X3
|
||||
#include <boost/version.hpp>
|
||||
|
||||
#if BOOST_VERSION >= 103800
|
||||
# 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;
|
||||
#if BOOST_VERSION < 106100
|
||||
# error "Boost version 1.61.0 or higher is required for Spirit X3. Please upgrade Boost."
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
/**
|
||||
* \brief Iterator type for parsing characters.
|
||||
*/
|
||||
typedef char const* iterator_t;
|
||||
#include "PFunctionAst.h"
|
||||
#include <boost/spirit/home/x3.hpp>
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
/**
|
||||
* \brief Type for parse tree matching results.
|
||||
*/
|
||||
typedef tree_match<iterator_t> parse_tree_match_t;
|
||||
namespace x3 = boost::spirit::x3;
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
/**
|
||||
* \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;
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
/**
|
||||
* \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>
|
||||
namespace musrfit { namespace grammar
|
||||
{
|
||||
static const int realID = 1; ///< Identifier for real number literals
|
||||
static const int constPiID = 2; ///< Identifier for PI constant
|
||||
static const int constGammaMuID = 3; ///< Identifier for GAMMA_MU constant (muon gyromagnetic ratio)
|
||||
static const int constFieldID = 4; ///< Identifier for magnetic field constant (B or -B)
|
||||
static const int constEnergyID = 5; ///< Identifier for energy constant (EN or -EN)
|
||||
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
|
||||
using x3::int_;
|
||||
using x3::double_;
|
||||
using x3::lit;
|
||||
using x3::lexeme;
|
||||
using x3::attr;
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
/**
|
||||
* \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
|
||||
* and build an abstract syntax tree (AST) for function expressions.
|
||||
*
|
||||
* \tparam ScannerT Scanner type used by Boost.Spirit for parsing
|
||||
*/
|
||||
template <typename ScannerT>
|
||||
struct definition
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Symbol tables - using inline to avoid multiple definition errors
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* @brief Symbol table for additive operators (+ and -).
|
||||
*
|
||||
* Maps operator characters to their corresponding AST token types
|
||||
* for addition and subtraction operations.
|
||||
*/
|
||||
struct additive_op_ : x3::symbols<ast::optoken>
|
||||
{
|
||||
additive_op_()
|
||||
{
|
||||
//--------------------------------------------------------------------
|
||||
/**
|
||||
* \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 ];
|
||||
add("+", ast::op_plus)("-", ast::op_minus);
|
||||
}
|
||||
};
|
||||
|
||||
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") ] |
|
||||
leaf_node_d[ str_p("-B") ];
|
||||
inline multiplicative_op_ multiplicative_op; ///< Global instance of multiplicative operator symbol table
|
||||
|
||||
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 ] ) ] |
|
||||
leaf_node_d[ ( lexeme_d[ "-T" >> +digit_p ] ) ];
|
||||
inline fun_tok_ fun_tok; ///< Global instance of function name symbol table
|
||||
|
||||
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 ] ) |
|
||||
( lexeme_d[ "-PAR" >> +digit_p ] ) ];
|
||||
inline const_tok_ const_tok; ///< Global instance of constant name symbol table
|
||||
|
||||
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(')')
|
||||
| lexeme_d[ root_node_d[ str_p("SIN") ] >> ch_p('(') ] >> expression >> ch_p(')')
|
||||
| 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(')')
|
||||
;
|
||||
/// Top-level rule: FUN# = expression
|
||||
x3::rule<class assignment, ast::assignment> const assignment = "assignment";
|
||||
|
||||
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
|
||||
| const_pi
|
||||
| const_gamma_mu
|
||||
| const_field
|
||||
| const_energy
|
||||
| const_temp
|
||||
| parameter
|
||||
| map
|
||||
| function
|
||||
| power
|
||||
| inner_node_d[ch_p('(') >> expression >> ch_p(')')]
|
||||
;
|
||||
/// Term with multiplication/division (higher precedence)
|
||||
x3::rule<class term, ast::expression> const term = "term";
|
||||
|
||||
term = factor >>
|
||||
*( (root_node_d[ch_p('*')] >> factor)
|
||||
| (root_node_d[ch_p('/')] >> factor)
|
||||
);
|
||||
/// Factor: literal, constant, parameter, function, or parenthesized expression (highest precedence)
|
||||
x3::rule<class factor, ast::operand> const factor = "factor";
|
||||
|
||||
expression = term >>
|
||||
*( (root_node_d[ch_p('+')] >> term)
|
||||
| (root_node_d[ch_p('-')] >> term)
|
||||
);
|
||||
/// Constant: PI, GAMMA_MU, B, EN, or T#
|
||||
x3::rule<class constant, ast::constant> const constant = "constant";
|
||||
|
||||
assignment = (fun_label >> ch_p('=') >> expression);
|
||||
// End grammar definition
|
||||
/// Parameter reference: PAR# or -PAR#
|
||||
x3::rule<class parameter, ast::parameter> const parameter = "parameter";
|
||||
|
||||
// turn on the debugging info.
|
||||
BOOST_SPIRIT_DEBUG_RULE(real);
|
||||
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);
|
||||
}
|
||||
/// Map reference: MAP# or -MAP#
|
||||
x3::rule<class map_ref, ast::map_ref> const map = "map";
|
||||
|
||||
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
|
||||
/// Function call: FUNC(expression)
|
||||
x3::rule<class function_call, ast::function_call> const function = "function";
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
/**
|
||||
* \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; }
|
||||
};
|
||||
};
|
||||
/// 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
|
||||
| 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_
|
||||
|
||||
Reference in New Issue
Block a user