Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 4adf66cb26 | |||
| 8a86351674 | |||
| 418ca6b0a1 | |||
| 73aff4ec69 | |||
| 7ce0926fd9 |
@@ -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
@@ -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::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
306
src/include/PFunctionAst.h
Normal 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_
|
||||
@@ -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_
|
||||
|
||||
@@ -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
|
||||
|
||||
/**
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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_
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}}
|
||||
|
||||
|
||||
|
||||
@@ -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_
|
||||
|
||||
@@ -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_
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}}
|
||||
|
||||
|
||||
|
||||
@@ -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>;
|
||||
|
||||
@@ -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>;
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user