5 Commits
root6 ... x3

16 changed files with 1383 additions and 1316 deletions

View File

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

File diff suppressed because it is too large Load Diff

View File

@@ -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;
}

View File

@@ -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::leer&) 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.)
};

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
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_

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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