From a040f8b576999698bc82ee55d157b1112638e413 Mon Sep 17 00:00:00 2001 From: Andreas Suter Date: Sat, 27 Dec 2025 13:57:14 +0100 Subject: [PATCH] added doxygen docu to spirit X3 under tests. --- src/tests/spirit/PFunction.cpp | 105 +++++++++++++++++- src/tests/spirit/PFunction.h | 129 +++++++++++++++++++--- src/tests/spirit/PFunctionAst.h | 2 +- src/tests/spirit/PFunctionGrammar.h | 148 ++++++++++++++++++++++++-- src/tests/spirit/PFunctionHandler.cpp | 103 ++++++++++++------ src/tests/spirit/PFunctionHandler.h | 118 ++++++++++++++++++-- src/tests/spirit/spirit_fcn_test.cpp | 90 ++++++++++++++++ 7 files changed, 629 insertions(+), 66 deletions(-) diff --git a/src/tests/spirit/PFunction.cpp b/src/tests/spirit/PFunction.cpp index df5740cba..f99f7f6f1 100644 --- a/src/tests/spirit/PFunction.cpp +++ b/src/tests/spirit/PFunction.cpp @@ -41,7 +41,19 @@ namespace x3 = boost::spirit::x3; //-------------------------------------------------------------------------- -// Constructor +/** + * @brief Constructs a PFunction by parsing the input expression. + * + * Parses the input string using the Boost.Spirit X3 grammar to extract + * the function assignment (FUN# = expression) and build the AST. The + * constructor validates the parse and stores the function number and + * expression tree for later evaluation. + * + * @param input Function assignment string (e.g., "FUN1 = PAR1 * COS(PAR2)") + * @param param Vector of parameter values for PAR# references + * @param map Vector of map indices for MAP# indirect references + * @param debug Enable debug output for parsing and evaluation + */ //-------------------------------------------------------------------------- PFunction::PFunction(const std::string& input, std::vector param, std::vector map, bool debug) @@ -85,7 +97,11 @@ PFunction::PFunction(const std::string& input, std::vector param, } //-------------------------------------------------------------------------- -// Destructor +/** + * @brief Destructor - releases resources. + * + * Clears the parameter and map vectors to free memory. + */ //-------------------------------------------------------------------------- PFunction::~PFunction() { @@ -94,7 +110,15 @@ PFunction::~PFunction() } //-------------------------------------------------------------------------- -// Eval +/** + * @brief Evaluates the parsed expression using current parameter values. + * + * Traverses the AST using the visitor pattern to compute the numeric + * result. The evaluation applies operators left-to-right with proper + * precedence as encoded in the AST structure. + * + * @return The computed value, or 0.0 if the function is invalid + */ //-------------------------------------------------------------------------- double PFunction::Eval() { @@ -133,16 +157,38 @@ double PFunction::Eval() // EvalVisitor Implementation //========================================================================== +/** + * @brief Evaluates a nil (empty) AST node. + * + * @return Always returns 0.0 + */ double PFunction::EvalVisitor::operator()(const musrfit::ast::nil&) const { return 0.0; } +/** + * @brief Evaluates a numeric literal. + * + * @param val The literal value from the AST + * @return The literal value unchanged + */ double PFunction::EvalVisitor::operator()(double val) const { return val; } +/** + * @brief Evaluates a constant (PI, GAMMA_MU, B, EN, T#). + * + * Converts symbolic constants to their numeric values. Supports: + * - PI: Mathematical constant π + * - GAMMA_MU: Muon gyromagnetic ratio + * - B, EN, T#: Not supported in test version (returns 0.0) + * + * @param c The constant AST node with type, sign, and optional index + * @return The constant's numeric value (with sign applied if negative) + */ double PFunction::EvalVisitor::operator()(const musrfit::ast::constant& c) const { double val = 0.0; @@ -171,6 +217,16 @@ double PFunction::EvalVisitor::operator()(const musrfit::ast::constant& c) const return c.sign ? -val : val; } +/** + * @brief Evaluates a parameter reference (PAR#). + * + * Looks up the parameter value from the fParam vector using the + * parameter number (1-based indexing in syntax, 0-based in vector). + * Validates bounds and applies sign. + * + * @param p The parameter AST node containing number and sign + * @return The parameter value (with sign applied), or 0.0 if out of bounds + */ double PFunction::EvalVisitor::operator()(const musrfit::ast::parameter& p) const { int idx = p.number - 1; @@ -184,6 +240,16 @@ double PFunction::EvalVisitor::operator()(const musrfit::ast::parameter& p) cons return p.sign ? -val : val; } +/** + * @brief Evaluates a map reference (MAP#) for indirect parameter lookup. + * + * Performs a two-level lookup: first looks up the map index in fMap, + * then uses that value to index into fParam. This allows for flexible + * parameter remapping. Validates both lookups and applies sign. + * + * @param m The map reference AST node containing map number and sign + * @return The indirectly referenced parameter value (with sign), or 0.0 if invalid + */ double PFunction::EvalVisitor::operator()(const musrfit::ast::map_ref& m) const { int mapIdx = m.number - 1; @@ -204,6 +270,16 @@ double PFunction::EvalVisitor::operator()(const musrfit::ast::map_ref& m) const return m.sign ? -val : val; } +/** + * @brief Evaluates a function call (COS, SIN, EXP, SQRT, etc.). + * + * Recursively evaluates the argument expression, then applies the + * specified mathematical function. Supports trigonometric, hyperbolic, + * inverse, exponential, and logarithmic functions. + * + * @param f The function call AST node with function ID and argument expression + * @return The result of applying the mathematical function to the argument + */ double PFunction::EvalVisitor::operator()(const musrfit::ast::function_call& f) const { // Evaluate argument @@ -245,6 +321,16 @@ double PFunction::EvalVisitor::operator()(const musrfit::ast::function_call& f) return result; } +/** + * @brief Evaluates a power operation POW(base, exponent). + * + * Recursively evaluates both the base and exponent expressions, + * then computes base^exponent using std::pow. Uses absolute value + * of base to avoid complex number issues with negative bases. + * + * @param p The power call AST node with base and exponent expressions + * @return The result of raising the base to the exponent power + */ double PFunction::EvalVisitor::operator()(const musrfit::ast::power_call& p) const { // Evaluate base @@ -278,6 +364,19 @@ double PFunction::EvalVisitor::operator()(const musrfit::ast::power_call& p) con return std::pow(std::abs(base), exponent); } +/** + * @brief Evaluates a complete expression with binary operators. + * + * Evaluates the first operand, then applies each operation in sequence + * from left to right. The AST structure encodes operator precedence, + * so this left-to-right evaluation produces correct results. + * + * Handles division by zero by checking if divisor magnitude is greater + * than 1e-20; otherwise returns 0.0 for that sub-expression. + * + * @param e The expression AST node with first operand and operation list + * @return The computed result of the expression + */ double PFunction::EvalVisitor::operator()(const musrfit::ast::expression& e) const { double result = boost::apply_visitor(*this, e.first); diff --git a/src/tests/spirit/PFunction.h b/src/tests/spirit/PFunction.h index 10c737fa0..66c89c9f1 100644 --- a/src/tests/spirit/PFunction.h +++ b/src/tests/spirit/PFunction.h @@ -41,47 +41,152 @@ //---------------------------------------------------------------------------- /** - * \brief Test version of PFunction class - simplified without PMetaData. + * @brief Test version of PFunction class - simplified without PMetaData. * * This test version uses the same X3 grammar and AST but with a simpler - * interface for testing purposes. It does not support metadata (field, - * energy, temperature). + * interface for testing purposes. It demonstrates the Boost.Spirit X3 parser + * and AST-based expression evaluation without the complexity of experimental + * metadata integration. + * + * Key features: + * - Parses function assignments (FUN# = expression) + * - Supports arithmetic operators: +, -, *, / + * - Supports mathematical functions: cos, sin, tan, exp, log, sqrt, pow, etc. + * - Supports parameter references: PAR# + * - Supports map references: MAP# (indirect parameter lookup) + * - Supports constants: PI, GAMMA_MU + * - Evaluates expressions using the visitor pattern + * + * Limitations compared to production version: + * - No metadata support (field B, energy EN, temperature T#) + * - Simplified error handling + * - Test/demonstration purposes only */ class PFunction { public: + /** + * @brief Constructs a PFunction by parsing and validating an input expression. + * + * @param input The function expression string to parse (e.g., "FUN1 = PAR1 * COS(PAR2)") + * @param param Vector of parameter values referenced by PAR# in expressions + * @param map Vector of map indices for indirect parameter references (MAP#) + * @param debug Enable debug output showing parse tree and evaluation steps + */ PFunction(const std::string& input, std::vector param, std::vector map, bool debug = false); + + /** + * @brief Destructor - cleans up parameter and map vectors. + */ virtual ~PFunction(); + /** + * @brief Checks if the function was successfully parsed and is valid. + * + * @return true if parsing succeeded and the function is ready for evaluation + */ virtual bool IsValid() { return fValid; } + + /** + * @brief Gets the function number from the parsed assignment. + * + * @return The function number extracted from FUN# in the parsed expression + */ virtual int GetFuncNo() { return fFuncNo; } + + /** + * @brief Evaluates the parsed expression with current parameter values. + * + * @return The numeric result of evaluating the expression, or 0.0 if invalid + */ virtual double Eval(); private: - musrfit::ast::expression fAst; - std::vector fParam; - std::vector fMap; - bool fValid; - int fFuncNo; - bool fDebug; + musrfit::ast::expression fAst; ///< Abstract syntax tree of the parsed expression + std::vector fParam; ///< Parameter values for PAR# references + std::vector fMap; ///< Map indices for MAP# indirect references + bool fValid; ///< Validity flag indicating successful parsing + int fFuncNo; ///< Function number extracted from FUN# + bool fDebug; ///< Debug mode flag for verbose output - // Simplified evaluation visitor (no metadata support) + /** + * @brief Visitor class for evaluating AST nodes to numeric values. + * + * Implements the visitor pattern using boost::static_visitor to traverse + * the AST and compute the result. Each operator() overload handles a + * different AST node type (literals, constants, parameters, operators, etc.). + * + * This simplified version does not support experimental metadata values. + */ class EvalVisitor : public boost::static_visitor { public: + /** + * @brief Constructs an evaluation visitor with parameter and map contexts. + * + * @param map Vector of map indices for MAP# lookups + * @param param Vector of parameter values for PAR# lookups + */ EvalVisitor(const std::vector& map, const std::vector& param) : fMap(map), fParam(param) {} + /** + * @brief Evaluates a nil/empty AST node. + * @param AST nil node (unused) + * @return 0.0 + */ double operator()(const musrfit::ast::nil&) const; + + /** + * @brief Evaluates a numeric literal. + * @param val The literal value + * @return The literal value unchanged + */ double operator()(double val) const; + + /** + * @brief Evaluates a constant (PI, GAMMA_MU, etc.). + * @param c The constant AST node + * @return The constant's numeric value with sign applied + */ double operator()(const musrfit::ast::constant& c) const; + + /** + * @brief Evaluates a parameter reference (PAR#). + * @param p The parameter AST node containing the parameter number + * @return The parameter value with sign applied + */ double operator()(const musrfit::ast::parameter& p) const; + + /** + * @brief Evaluates a map reference (MAP#) for indirect parameter lookup. + * @param m The map reference AST node + * @return The parameter value indexed through the map with sign applied + */ double operator()(const musrfit::ast::map_ref& m) const; + + /** + * @brief Evaluates a function call (COS, SIN, EXP, etc.). + * @param f The function call AST node containing function ID and argument + * @return The result of applying the mathematical function + */ double operator()(const musrfit::ast::function_call& f) const; + + /** + * @brief Evaluates a power operation POW(base, exponent). + * @param p The power call AST node with base and exponent expressions + * @return The result of base raised to the exponent power + */ double operator()(const musrfit::ast::power_call& p) const; + + /** + * @brief Evaluates a complete expression with operators. + * @param e The expression AST node with operands and operations + * @return The result of evaluating the expression left-to-right + */ double operator()(const musrfit::ast::expression& e) const; private: - const std::vector& fMap; - const std::vector& fParam; + const std::vector& fMap; ///< Reference to map vector for MAP# lookups + const std::vector& fParam; ///< Reference to parameter vector for PAR# lookups }; }; diff --git a/src/tests/spirit/PFunctionAst.h b/src/tests/spirit/PFunctionAst.h index ff82058f2..590e8d1ed 100644 --- a/src/tests/spirit/PFunctionAst.h +++ b/src/tests/spirit/PFunctionAst.h @@ -11,7 +11,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 * diff --git a/src/tests/spirit/PFunctionGrammar.h b/src/tests/spirit/PFunctionGrammar.h index bff81847a..5093eaacb 100644 --- a/src/tests/spirit/PFunctionGrammar.h +++ b/src/tests/spirit/PFunctionGrammar.h @@ -11,7 +11,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 * @@ -44,6 +44,17 @@ namespace x3 = boost::spirit::x3; +/** + * @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. + */ namespace musrfit { namespace grammar { using x3::int_; @@ -56,6 +67,12 @@ namespace musrfit { namespace grammar // 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 { additive_op_() @@ -64,8 +81,14 @@ namespace musrfit { namespace grammar } }; - inline additive_op_ additive_op; + inline additive_op_ additive_op; ///< Global instance of additive operator symbol table + /** + * @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 { multiplicative_op_() @@ -74,8 +97,15 @@ namespace musrfit { namespace grammar } }; - inline multiplicative_op_ multiplicative_op; + inline multiplicative_op_ multiplicative_op; ///< Global instance of multiplicative operator symbol table + /** + * @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 { fun_tok_() @@ -100,8 +130,14 @@ namespace musrfit { namespace grammar } }; - inline fun_tok_ fun_tok; + inline fun_tok_ fun_tok; ///< Global instance of function name symbol table + /** + * @brief Symbol table for named constants. + * + * Maps constant names (PI, GAMMA_MU) to their corresponding + * AST constant type identifiers. + */ struct const_tok_ : x3::symbols { const_tok_() @@ -110,35 +146,77 @@ namespace musrfit { namespace grammar } }; - inline const_tok_ const_tok; + inline const_tok_ const_tok; ///< Global instance of constant name symbol table /////////////////////////////////////////////////////////////////////////// - // Rules + // Grammar Rules - Forward Declarations /////////////////////////////////////////////////////////////////////////// + /// Top-level rule: FUN# = expression x3::rule const assignment = "assignment"; + + /// Expression with addition/subtraction (lowest precedence) x3::rule const expression = "expression"; + + /// Term with multiplication/division (higher precedence) x3::rule const term = "term"; + + /// Factor: literal, constant, parameter, function, or parenthesized expression (highest precedence) x3::rule const factor = "factor"; + + /// Constant: PI, GAMMA_MU, B, EN, or T# x3::rule const constant = "constant"; + + /// Parameter reference: PAR# or -PAR# x3::rule const parameter = "parameter"; + + /// Map reference: MAP# or -MAP# x3::rule const map = "map"; + + /// Function call: FUNC(expression) x3::rule const function = "function"; + + /// Power operation: POW(base, exponent) x3::rule const power = "power"; /////////////////////////////////////////////////////////////////////////// - // Rule definitions + // 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 @@ -148,6 +226,12 @@ namespace musrfit { namespace grammar | 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)) @@ -157,20 +241,51 @@ namespace musrfit { namespace grammar | (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, @@ -185,8 +300,25 @@ namespace musrfit { namespace grammar }} +/** + * @namespace musrfit + * @brief Top-level namespace for musrfit components. + */ namespace musrfit { - // Accessor for the top-level rule + /** + * @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; diff --git a/src/tests/spirit/PFunctionHandler.cpp b/src/tests/spirit/PFunctionHandler.cpp index ae7199c59..fb02b2bdc 100644 --- a/src/tests/spirit/PFunctionHandler.cpp +++ b/src/tests/spirit/PFunctionHandler.cpp @@ -5,12 +5,10 @@ Author: Andreas Suter e-mail: andreas.suter@psi.ch - $Id$ - ***************************************************************************/ /*************************************************************************** - * Copyright (C) 2007 by Andreas Suter * + * Copyright (C) 2007-2026 by Andreas Suter * * andreas.suter@psi.c * * * * This program is free software; you can redistribute it and/or modify * @@ -38,14 +36,19 @@ #include "PFunctionHandler.h" -//------------------------------------------------------------- -// Constructor //------------------------------------------------------------- /** - *

+ * @brief Constructs a PFunctionHandler from a file. * - * \param fln + * Reads the specified file to extract parameter values (PAR block), + * map indices (MAP block), and function definitions (FUNCTIONS block). + * Validates that all required blocks are present and that map indices + * are within parameter bounds. + * + * @param fln Filename containing the function definitions + * @param debug Enable debug output showing parsing details */ +//------------------------------------------------------------- PFunctionHandler::PFunctionHandler(char *fln, bool debug) : fDebug(debug), fFileName(fln) { fValid = true; @@ -58,14 +61,18 @@ PFunctionHandler::PFunctionHandler(char *fln, bool debug) : fDebug(debug), fFile fValid = MapsAreValid(); } -//------------------------------------------------------------- -// Constructor //------------------------------------------------------------- /** - *

+ * @brief Constructs a PFunctionHandler from a vector of input lines. * - * \param lines + * Parses the provided lines to extract PAR, MAP, and FUNCTIONS blocks. + * This constructor is useful for testing or when input comes from + * sources other than files. Validates that all required blocks are + * present and that map indices are within parameter bounds. + * + * @param lines Vector of strings containing the input (PAR, MAP, FUNCTIONS blocks) */ +//------------------------------------------------------------- PFunctionHandler::PFunctionHandler(std::vector lines) { fValid = true; @@ -152,13 +159,13 @@ PFunctionHandler::PFunctionHandler(std::vector lines) } } -//------------------------------------------------------------- -// Destructor //------------------------------------------------------------- /** - *

+ * @brief Destructor - releases all resources. * + * Clears parameter, map, line, and function vectors to free memory. */ +//------------------------------------------------------------- PFunctionHandler::~PFunctionHandler() { std::cout << std::endl << "in ~PFunctionHandler()" << std::endl << std::endl; @@ -169,13 +176,19 @@ PFunctionHandler::~PFunctionHandler() fFuncs.clear(); } -//------------------------------------------------------------- -// DoParse (public) //------------------------------------------------------------- /** - *

+ * @brief Parses all function definitions using the X3 grammar. * + * Iterates through all function lines, parsing each with the Spirit X3 + * grammar to create PFunction objects. Performs validation checks: + * - All functions parse successfully + * - Parameter and map indices are within bounds + * - Function numbers are unique (no duplicates) + * + * @return true if all functions parsed and validated successfully */ +//------------------------------------------------------------- bool PFunctionHandler::DoParse() { std::cout << std::endl << "in PFunctionHandler::DoParse() ..."; @@ -236,14 +249,17 @@ bool PFunctionHandler::DoParse() return success; } -//------------------------------------------------------------- -// Eval (public) //------------------------------------------------------------- /** - *

+ * @brief Evaluates a function by its function number. * - * \param i + * Looks up the function in the internal vector using its function number + * (from the FUN# declaration) and evaluates it with current parameter values. + * + * @param i Function number to evaluate + * @return The evaluation result, or 0.0 if function not found */ +//------------------------------------------------------------- double PFunctionHandler::Eval(int i) { if (GetFuncIndex(i) == -1) { @@ -254,14 +270,14 @@ double PFunctionHandler::Eval(int i) return fFuncs[GetFuncIndex(i)].Eval(); } -//------------------------------------------------------------- -// GetFuncNo (public) //------------------------------------------------------------- /** - *

+ * @brief Gets the function number of the i-th parsed function. * - * \param i + * @param i Index into the internal function vector (0-based) + * @return The function number (from FUN#), or -1 if index out of bounds */ +//------------------------------------------------------------- unsigned int PFunctionHandler::GetFuncNo(unsigned int i) { if (i > fFuncs.size()) @@ -270,13 +286,22 @@ unsigned int PFunctionHandler::GetFuncNo(unsigned int i) return fFuncs[i].GetFuncNo(); } -//------------------------------------------------------------- -// ReadFile (private) //------------------------------------------------------------- /** - *

+ * @brief Reads and parses the input file. * + * Opens the file specified in fFileName and extracts: + * - PAR block: parameter values (up to 10 per line) + * - MAP block: map indices (up to 10 per line) + * - FUNCTIONS block: function definition lines + * + * Lines starting with # are treated as comments and ignored. + * Processing stops when END tag is encountered. + * Converts all input to uppercase for case-insensitive parsing. + * + * @return true if file was successfully read and all required blocks found */ +//------------------------------------------------------------- bool PFunctionHandler::ReadFile() { std::cout << std::endl << "in ~PFunctionHandler::ReadFile()"; @@ -377,13 +402,17 @@ bool PFunctionHandler::ReadFile() return success; } -//------------------------------------------------------------- -// MapsAreValid (private) //------------------------------------------------------------- /** - *

+ * @brief Validates that all map indices are within parameter bounds. * + * Checks that each value in the MAP block is a valid parameter index. + * Map values use 1-based indexing, so valid values are 1 to fParam.size(). + * This validation prevents out-of-bounds access during function evaluation. + * + * @return true if all map indices are valid, false otherwise */ +//------------------------------------------------------------- bool PFunctionHandler::MapsAreValid() { bool success = true; @@ -399,14 +428,18 @@ bool PFunctionHandler::MapsAreValid() return success; } -//------------------------------------------------------------- -// GetFuncIndex (private) //------------------------------------------------------------- /** - *

+ * @brief Finds the internal vector index for a function by its number. * - * \param funcNo + * Searches the fFuncs vector for a function with the specified function + * number. This maps from the user-visible function number (FUN#) to the + * internal 0-based vector index. + * + * @param funcNo Function number to search for (from FUN# declaration) + * @return Index in fFuncs vector (0-based), or -1 if not found */ +//------------------------------------------------------------- int PFunctionHandler::GetFuncIndex(int funcNo) { int index = -1; diff --git a/src/tests/spirit/PFunctionHandler.h b/src/tests/spirit/PFunctionHandler.h index bf817e5b0..80c741bd4 100644 --- a/src/tests/spirit/PFunctionHandler.h +++ b/src/tests/spirit/PFunctionHandler.h @@ -37,33 +37,137 @@ #include "PFunctionGrammar.h" #include "PFunction.h" +/** + * @brief Manages parsing and evaluation of multiple function definitions. + * + * This class handles the complete workflow for processing function blocks + * from msr files or direct input. It: + * - Reads function definitions from file or vector of strings + * - Parses parameter (PAR) and map (MAP) blocks + * - Validates map indices against parameter bounds + * - Creates PFunction objects for each function definition + * - Provides evaluation interface for functions by number + * + * Input format: + * @code + * PAR ... + * MAP ... + * FUNCTIONS + * FUN1 = expression1 + * FUN2 = expression2 + * ... + * END + * @endcode + */ class PFunctionHandler { public: + /** + * @brief Constructs handler by reading function definitions from a file. + * + * @param fln Filename containing PAR, MAP, and FUNCTIONS blocks + * @param debug Enable debug output during parsing and evaluation + */ PFunctionHandler(char *fln, bool debug); + + /** + * @brief Constructs handler from a vector of input lines. + * + * Useful for testing or when input comes from sources other than files. + * + * @param lines Vector of strings containing PAR, MAP, and FUNCTIONS blocks + */ PFunctionHandler(std::vector lines); + + /** + * @brief Destructor - cleans up resources. + */ virtual ~PFunctionHandler(); + /** + * @brief Checks if initialization was successful. + * + * Returns false if file reading failed, required blocks are missing, + * or map validation failed. + * + * @return true if the handler is ready to parse and evaluate functions + */ virtual bool IsValid() { return fValid; } + + /** + * @brief Parses all function definitions and validates them. + * + * Creates PFunction objects for each function line, validates that + * all functions parsed successfully, and ensures function numbers + * are unique. + * + * @return true if all functions parsed successfully and are valid + */ virtual bool DoParse(); + + /** + * @brief Evaluates a function by its function number. + * + * @param i Function number (from FUN# in the definition) + * @return The evaluated result, or 0.0 if function not found + */ virtual double Eval(int i); + + /** + * @brief Gets the function number of the i-th parsed function. + * + * @param i Index into the internal function vector (0-based) + * @return The function number, or -1 if index out of bounds + */ virtual unsigned int GetFuncNo(unsigned int i); + + /** + * @brief Gets the total number of successfully parsed functions. + * + * @return Number of functions in the handler + */ virtual unsigned int GetNoOfFuncs() { return fFuncs.size(); } private: - bool fDebug; - bool fValid; + bool fDebug; ///< Debug mode flag for verbose output + bool fValid; ///< Validity flag indicating successful initialization - std::string fFileName; + std::string fFileName; ///< Input filename (empty if constructed from lines) - std::vector fParam; - std::vector fMap; - std::vector fLines; + std::vector fParam; ///< Parameter values from PAR block + std::vector fMap; ///< Map indices from MAP block + std::vector fLines; ///< Function definition lines from FUNCTIONS block - std::vector fFuncs; + std::vector fFuncs; ///< Parsed function objects + /** + * @brief Reads and parses the input file. + * + * Extracts PAR, MAP, and FUNCTIONS blocks from the file. Lines starting + * with # are treated as comments. Processing stops at END tag. + * + * @return true if file was read and all required blocks found + */ virtual bool ReadFile(); + + /** + * @brief Validates that all map indices are within parameter bounds. + * + * Ensures each MAP# value is a valid parameter index (1-based), + * preventing out-of-bounds access during evaluation. + * + * @return true if all map indices are valid + */ virtual bool MapsAreValid(); + + /** + * @brief Finds the internal index for a function by its function number. + * + * Maps from function number (FUN#) to the 0-based index in fFuncs vector. + * + * @param funcNo Function number to search for + * @return Index in fFuncs vector, or -1 if not found + */ virtual int GetFuncIndex(int funcNo); }; diff --git a/src/tests/spirit/spirit_fcn_test.cpp b/src/tests/spirit/spirit_fcn_test.cpp index 2b7074167..4080a6a52 100644 --- a/src/tests/spirit/spirit_fcn_test.cpp +++ b/src/tests/spirit/spirit_fcn_test.cpp @@ -1,3 +1,50 @@ +/*************************************************************************** + + spirit_fcn_test.cpp + + Author: Andreas Suter + e-mail: andreas.suter@psi.ch + +***************************************************************************/ + +/*************************************************************************** + * Copyright (C) 2007-2026 by Andreas Suter * + * andreas.suter@psi.c * + * * + * 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. * + ***************************************************************************/ + +/** + * @file spirit_fcn_test.cpp + * @brief Test program for the Boost.Spirit X3 function parser. + * + * This program demonstrates and tests the Spirit X3-based parser for + * mathematical function expressions used in musrfit. It supports both + * interactive mode and file-based input. + * + * Features: + * - Parses PAR (parameter), MAP, and FUNCTIONS blocks + * - Evaluates mathematical expressions with operators and functions + * - Supports debug mode to inspect AST without evaluation + * - Interactive input mode for testing expressions + * + * @author Andreas Suter + * @date 2025 + */ + #include #include @@ -6,6 +53,12 @@ #include "PFunctionHandler.h" //----------------------------------------------------- +/** + * @brief Displays command-line usage information. + * + * Prints help text showing available command-line options and + * their descriptions. + */ void syntax() { std::cout << std::endl << "spirit_fcn_test [--file [--debug]] | [--help]"; @@ -17,6 +70,23 @@ void syntax() } //----------------------------------------------------- +/** + * @brief Handles interactive input from the user. + * + * Prompts the user to enter PAR, MAP, and FUNCTIONS blocks line by line. + * Input continues until the user types ".q" to quit. This mode is useful + * for quick testing without creating input files. + * + * Expected input format: + * - PAR ... + * - MAP ... + * - FUNCTIONS + * - FUN# = + * - ... + * - END + * + * @param lines Reference to vector where input lines will be stored + */ void handle_input(std::vector &lines) { std::cout << std::endl << "will handle input ..."; @@ -46,6 +116,26 @@ void handle_input(std::vector &lines) } while (!done); } +//----------------------------------------------------- +/** + * @brief Main entry point for the spirit_fcn_test program. + * + * Parses command-line arguments to determine operating mode: + * - No arguments: Interactive mode (prompts user for input) + * - --file : Reads input from specified file + * - --debug: Enables debug output (AST inspection without evaluation) + * - --help: Shows usage information + * + * Workflow: + * 1. Parse command-line arguments + * 2. Create PFunctionHandler from file or interactive input + * 3. Parse all function definitions + * 4. If not in debug mode, evaluate all functions and display results + * + * @param argc Argument count + * @param argv Argument vector + * @return 1 on success, 0 on error or help display + */ //----------------------------------------------------- int main(int argc, char *argv[]) {