diff --git a/src/musredit_qt6/mupp/var/include/PAnnotation.hpp b/src/musredit_qt6/mupp/var/include/PAnnotation.hpp index de04e9fc6..261487ae3 100644 --- a/src/musredit_qt6/mupp/var/include/PAnnotation.hpp +++ b/src/musredit_qt6/mupp/var/include/PAnnotation.hpp @@ -42,40 +42,89 @@ namespace mupp { /////////////////////////////////////////////////////////////////////////////// - // The annotation handler links the AST to a map of iterator positions - // for the purpose of subsequent semantic error handling when the - // program is being compiled. + /** + * @brief The PAnnotation struct links AST nodes to source code positions. + * + * The annotation handler associates each AST node with its corresponding + * iterator position in the source code. This mapping enables accurate error + * reporting during semantic analysis and compilation by allowing the error + * 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. + * + * @tparam Iterator the iterator type for traversing the source code (typically std::string::const_iterator) + */ /////////////////////////////////////////////////////////////////////////////// template struct PAnnotation { + /// Result type specification required by Boost.Phoenix function protocol template struct result { typedef void type; }; - std::vector& iters; + std::vector& iters; ///< Reference to vector storing iterator positions indexed by AST node IDs + + /** + * @brief Constructor that initializes the annotation handler. + * @param iters reference to vector that will store iterator positions for each annotated AST node + */ PAnnotation(std::vector& iters) : iters(iters) {} + /** + * @brief Helper struct to set ID tags on AST nodes. + * + * Uses template metaprogramming to selectively assign IDs only to nodes + * that inherit from ast::tagged, avoiding overhead for non-tagged nodes. + */ struct set_id { - typedef void result_type; + typedef void result_type; ///< Return type for Boost.Phoenix compatibility - int id; + int id; ///< The ID to assign to tagged AST nodes + + /** + * @brief Constructor that stores the ID to be assigned. + * @param id the unique identifier corresponding to a source position + */ set_id(int id) : id(id) {} + /** + * @brief Dispatches to the appropriate handler based on node type. + * + * Uses boost::is_base_of to determine at compile time whether the node + * inherits from ast::tagged. + * @tparam T the AST node type + * @param x reference to the AST node to potentially tag + */ template void operator()(T& x) const { this->dispatch(x, boost::is_base_of()); } - // This will catch all nodes except those inheriting from ast::tagged + /** + * @brief No-op handler for non-tagged AST nodes. + * + * This overload is selected at compile time for nodes that don't + * inherit from ast::tagged. + * @tparam T the non-tagged AST node type + * @param x reference to the AST node (unused) + */ template void dispatch(T& x, boost::mpl::false_) const { // (no-op) no need for tags } - // This will catch all nodes inheriting from ast::tagged + /** + * @brief Assigns ID to tagged AST nodes. + * + * This overload is selected at compile time for nodes that inherit + * from ast::tagged, setting their id field. + * @tparam T the tagged AST node type + * @param x reference to the AST node whose id field will be set + */ template void dispatch(T& x, boost::mpl::true_) const { @@ -83,6 +132,14 @@ namespace mupp } }; + /** + * @brief Annotates an operand AST node with its source position. + * + * Stores the iterator position and assigns a unique ID to the operand node + * by applying the set_id visitor to handle different operand variant types. + * @param ast reference to the operand AST node to annotate + * @param pos iterator pointing to the operand's position in the source code + */ void operator()(ast::operand& ast, Iterator pos) const { int id = iters.size(); @@ -90,6 +147,14 @@ namespace mupp boost::apply_visitor(set_id(id), ast); } + /** + * @brief Annotates an assignment AST node with its source position. + * + * Stores the iterator position and assigns a unique ID to the left-hand + * side variable of the assignment. + * @param ast reference to the assignment AST node to annotate + * @param pos iterator pointing to the assignment's position in the source code + */ void operator()(ast::assignment& ast, Iterator pos) const { int id = iters.size(); diff --git a/src/musredit_qt6/mupp/var/include/PAst.hpp b/src/musredit_qt6/mupp/var/include/PAst.hpp index 01de54f40..f30ae9e0f 100644 --- a/src/musredit_qt6/mupp/var/include/PAst.hpp +++ b/src/musredit_qt6/mupp/var/include/PAst.hpp @@ -43,119 +43,243 @@ namespace mupp { namespace ast { /////////////////////////////////////////////////////////////////////////// - // The AST + /** + * @brief Abstract Syntax Tree (AST) definitions for the variable parser. + * + * This namespace defines the complete AST structure used to represent + * parsed variable expressions. The AST is built using Boost.Variant for + * type-safe unions and Boost.Spirit's automatic AST generation from + * grammar rules. + * + * The AST supports: + * - Arithmetic operations: +, -, *, / + * - Unary operations: +, - + * - Mathematical functions: sin, cos, tan, exp, log, sqrt, etc. + * - Power operations: pow(base, exponent) + * - Variable declarations and assignments + * - Expression evaluation with proper precedence + */ /////////////////////////////////////////////////////////////////////////// + + /** + * @brief Base struct for AST nodes that require position annotation. + * + * Tagged nodes store an ID that maps to their position in the source code. + * This enables accurate error reporting by the error handler. Not all AST + * nodes need to be tagged; only those that may generate semantic errors. + */ struct tagged { - int id; // Used to annotate the AST with the iterator position. - // This id is used as a key to a map - // (not really part of the AST.) + int id; ///< ID used as key to map to iterator position in source code (not part of the logical AST structure) }; + /** + * @brief Enumeration of arithmetic and unary operators. + * + * These tokens represent the fundamental operations supported by the + * expression evaluator. + */ enum optoken { - op_plus, - op_minus, - op_times, - op_divide, - op_positive, - op_negative, + op_plus, ///< Addition operator (+) + op_minus, ///< Subtraction operator (-) + op_times, ///< Multiplication operator (*) + op_divide, ///< Division operator (/) + op_positive, ///< Unary plus operator (+x) + op_negative, ///< Unary minus operator (-x) }; + /** + * @brief Enumeration of supported mathematical functions. + * + * These function identifiers map to standard mathematical operations + * evaluated during the semantic analysis phase. + */ enum funid { - fun_max, - fun_min, - fun_abs, - fun_sin, - fun_cos, - fun_tan, - fun_sinh, - fun_cosh, - fun_tanh, - fun_asin, - fun_acos, - fun_atan, - fun_exp, - fun_log, - fun_ln, - fun_sqrt + fun_max, ///< Maximum value function + fun_min, ///< Minimum value function + fun_abs, ///< Absolute value function + fun_sin, ///< Sine function + fun_cos, ///< Cosine function + fun_tan, ///< Tangent function + fun_sinh, ///< Hyperbolic sine function + fun_cosh, ///< Hyperbolic cosine function + fun_tanh, ///< Hyperbolic tangent function + fun_asin, ///< Arcsine function + fun_acos, ///< Arccosine function + fun_atan, ///< Arctangent function + fun_exp, ///< Exponential function + fun_log, ///< Base-10 logarithm function + fun_ln, ///< Natural logarithm 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 nil {}; + + // Forward declarations for recursive AST structures struct unary; struct expression; struct function; struct power; + /** + * @brief Represents a variable in an expression. + * + * Variables are identifiers prefixed with '$' in the source code. + * They reference parameters from the collection or previously declared + * variables. Inherits from tagged to support error reporting. + */ struct variable : tagged { + /** + * @brief Constructor for variable node. + * @param name the variable name (without the leading '$') + */ variable(std::string const& name = "") : name(name) {} - std::string name; + std::string name; ///< Variable name without the '$' prefix }; + /** + * @brief Variant type representing any operand in an expression. + * + * An operand can be a literal number, variable, function call, power + * operation, unary expression, or parenthesized expression. The variant + * uses recursive_wrapper for types that contain expressions to handle + * recursive grammar structures. + */ typedef boost::variant< - nil - , double - , variable - , boost::recursive_wrapper - , boost::recursive_wrapper - , boost::recursive_wrapper - , boost::recursive_wrapper + nil ///< Empty placeholder + , double ///< Numeric literal + , variable ///< Variable reference + , boost::recursive_wrapper ///< Function call (recursive) + , boost::recursive_wrapper ///< Power operation (recursive) + , boost::recursive_wrapper ///< Unary operation (recursive) + , boost::recursive_wrapper ///< Parenthesized expression (recursive) > operand; + /** + * @brief Represents a unary operation applied to an operand. + * + * Examples: -x, +y + */ struct unary { - optoken operator_; - operand operand_; + optoken operator_; ///< The unary operator (positive or negative) + operand operand_; ///< The operand the operator is applied to }; + /** + * @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_; - operand operand_; + 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; - std::list rest; + operand first; ///< The first operand in the expression + std::list rest; ///< Sequence of operations applied left-to-right }; + /** + * @brief Represents a function call with a single argument. + * + * Examples: sin($x), sqrt($y), abs($z) + */ struct function { - funid func_id; - expression arg; + 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 { - expression base; - expression pow; + expression base; ///< The base expression + expression pow; ///< The exponent expression }; + /** + * @brief Represents an assignment statement. + * + * Syntax: var_name = expression + * Assigns the result of evaluating the right-hand expression to an + * existing variable. + */ struct assignment { - variable lhs; - expression rhs; + variable lhs; ///< The left-hand side variable being assigned to + expression rhs; ///< The right-hand side expression to evaluate }; + /** + * @brief Represents a variable declaration with optional initialization. + * + * Syntax: var var_name = expression (with initialization) + * var var_name (without initialization) + * Declares a new variable and optionally initializes it with an expression. + */ struct variable_declaration { - variable lhs; - boost::optional rhs; + variable lhs; ///< The variable being declared + boost::optional rhs; ///< Optional initialization expression }; + /** + * @brief Variant type representing a single statement. + * + * A statement can be either a variable declaration or an assignment. + */ typedef boost::variant< - variable_declaration - , assignment> + variable_declaration ///< Variable declaration statement + , assignment> ///< Assignment statement statement; + /** + * @brief Type alias for a list of statements forming a program. + * + * The parser builds this list from the input, and the semantic analyzer + * processes it sequentially. + */ typedef std::list statement_list; - // print functions for debugging + /** + * @brief Stream output operator for nil nodes (debugging support). + * @param out the output stream + * @return the output stream for chaining + */ inline std::ostream& operator<<(std::ostream& out, nil) { out << std::string("nil"); return out; } + + /** + * @brief Stream output operator for variable nodes (debugging support). + * @param out the output stream + * @param var the variable node to output + * @return the output stream for chaining + */ inline std::ostream& operator<<(std::ostream& out, variable const& var) { out << var.name; return out; } }} diff --git a/src/musredit_qt6/mupp/var/include/PErrorHandler.hpp b/src/musredit_qt6/mupp/var/include/PErrorHandler.hpp index 5ae0302ad..5f770011e 100644 --- a/src/musredit_qt6/mupp/var/include/PErrorHandler.hpp +++ b/src/musredit_qt6/mupp/var/include/PErrorHandler.hpp @@ -41,17 +41,49 @@ namespace mupp { /////////////////////////////////////////////////////////////////////////////// - // The error handler + /** + * @brief The PErrorHandler struct handles parsing and semantic errors. + * + * 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 + * mechanism and works in conjunction with the PAnnotation handler to map + * AST node IDs back to source positions. + * + * Errors are appended to: ~/.musrfit/mupp/mupp_err.log + * + * @tparam Iterator the iterator type for the source code (typically std::string::const_iterator) + */ /////////////////////////////////////////////////////////////////////////////// template struct PErrorHandler { + /// Result type specification required by Boost.Phoenix function protocol template struct result { typedef void type; }; + /** + * @brief Constructor that stores the source code range. + * @param first iterator pointing to the beginning of the source code + * @param last iterator pointing to the end of the source code + */ PErrorHandler(Iterator first, Iterator last) : first(first), last(last) {} + /** + * @brief Function call operator that logs an error to file. + * + * Determines the line number and position of the error, formats a + * user-friendly error message with a visual indicator (^~~), and appends + * it to the error log file. Handles both mid-file errors and unexpected + * end-of-file errors. + * + * @tparam Message the message type (typically std::string) + * @tparam What the expected element type (typically std::string) + * @param message the error message prefix (e.g., "**ERROR** Expecting ") + * @param what description of what was expected at the error position + * @param err_pos iterator pointing to the position where the error occurred + */ template void operator()( Message const& message, @@ -76,6 +108,16 @@ namespace mupp } } + /** + * @brief Finds the start of the line containing an error and computes line number. + * + * Scans from the beginning of the source to the error position, counting + * line breaks (CR, LF, or CRLF) to determine the line number. + * + * @param err_pos iterator pointing to the error position + * @param line output parameter that will contain the 1-based line number + * @return iterator pointing to the start of the line containing the error + */ Iterator get_pos(Iterator err_pos, int& line) const { line = 1; @@ -99,6 +141,14 @@ namespace mupp return line_start; } + /** + * @brief Extracts the complete line containing an error. + * + * Returns the substring from the start of the line to the next line break. + * + * @param err_pos iterator pointing to the start of the line + * @return string containing the complete line (without line break characters) + */ std::string get_line(Iterator err_pos) const { Iterator i = err_pos; @@ -108,9 +158,9 @@ namespace mupp return std::string(err_pos, i); } - Iterator first; - Iterator last; - std::vector iters; + Iterator first; ///< Iterator to the beginning of the source code + Iterator last; ///< Iterator to the end of the source code + std::vector iters; ///< Vector mapping AST node IDs to source positions (used by PAnnotation) }; } diff --git a/src/musredit_qt6/mupp/var/include/PExpression.hpp b/src/musredit_qt6/mupp/var/include/PExpression.hpp index 39521d2b5..bf2e332eb 100644 --- a/src/musredit_qt6/mupp/var/include/PExpression.hpp +++ b/src/musredit_qt6/mupp/var/include/PExpression.hpp @@ -59,22 +59,51 @@ namespace mupp { namespace parser namespace ascii = boost::spirit::ascii; /////////////////////////////////////////////////////////////////////////////// - // The expression grammar + /** + * @brief The PExpression grammar for parsing mathematical expressions. + * + * This Boost.Spirit 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) + * - Multiplicative operators: *, / (medium precedence) + * - Additive operators: +, - (lowest precedence) + * + * The grammar supports: + * - Numeric literals (double) + * - Variable identifiers (prefixed with '$') + * - Mathematical functions: sin, cos, tan, exp, log, sqrt, etc. + * - Power function: pow(base, exponent) + * - Arithmetic operations with standard precedence + * + * Example expressions: + * - 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 struct PExpression : qi::grammar > { + /** + * @brief Constructor that initializes the grammar rules. + * @param error_handler reference to the error handler for reporting parse errors + */ PExpression(PErrorHandler& error_handler); - qi::symbols additive_op, multiplicative_op, unary_op; - qi::symbols fun_tok; + qi::symbols additive_op; ///< Symbol table for additive operators (+, -) + qi::symbols multiplicative_op; ///< Symbol table for multiplicative operators (*, /) + qi::symbols unary_op; ///< Symbol table for unary operators (+, -) + qi::symbols fun_tok; ///< Symbol table for function names - qi::rule > expr; - qi::rule > additive_expr; - qi::rule > multiplicative_expr; - qi::rule > unary_expr; - qi::rule > primary_expr; - qi::rule > identifier; + qi::rule > expr; ///< Top-level expression rule + qi::rule > additive_expr; ///< Additive expression rule (lowest precedence) + qi::rule > multiplicative_expr; ///< Multiplicative expression rule (medium precedence) + qi::rule > unary_expr; ///< Unary expression rule + qi::rule > primary_expr; ///< Primary expression rule (highest precedence) + qi::rule > identifier; ///< Identifier rule (variables prefixed with '$') }; }} diff --git a/src/musredit_qt6/mupp/var/include/PExpressionDef.hpp b/src/musredit_qt6/mupp/var/include/PExpressionDef.hpp index 8246efee7..1a7a29119 100644 --- a/src/musredit_qt6/mupp/var/include/PExpressionDef.hpp +++ b/src/musredit_qt6/mupp/var/include/PExpressionDef.hpp @@ -8,6 +8,19 @@ Based on Joel de Guzman example on calc7, 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. + + The grammar implements expression parsing with proper operator precedence: + - Primary expressions (literals, variables, functions) + - Unary operations (+, -) + - Multiplicative operations (*, /) + - Additive operations (+, -) + + Symbol tables are populated with operators and function names, and error + handling is integrated for reporting parse failures. + ***************************************************************************/ /*************************************************************************** diff --git a/src/musredit_qt6/mupp/var/include/PProgram.hpp b/src/musredit_qt6/mupp/var/include/PProgram.hpp index fe30fbc00..da2196735 100644 --- a/src/musredit_qt6/mupp/var/include/PProgram.hpp +++ b/src/musredit_qt6/mupp/var/include/PProgram.hpp @@ -48,39 +48,128 @@ namespace mupp { namespace prog { /////////////////////////////////////////////////////////////////////////// - // Variable Handler + /** + * @brief The PVarHandler class manages variable data during evaluation. + * + * This class stores a variable's name, values, and associated errors. + * Variables are vector-valued, where each element corresponds to data + * from a different run in the collection. The values and errors vectors + * must have the same size for the variable to be valid. + * + * This class is used by both the semantic analyzer (PProgram) to track + * declared variables and by the evaluator (PProgEval) to store and + * retrieve computed results. + */ /////////////////////////////////////////////////////////////////////////// class PVarHandler { public: + /** + * @brief Default constructor initializing an unnamed variable. + */ PVarHandler() : fName("") {} + /** + * @brief Sets the variable name. + * @param name the name to assign to this variable + */ void SetName(std::string name) { fName = name; } + + /** + * @brief Sets all values for this variable. + * @param dval vector of values to assign + */ void SetValue(std::vector &dval) { fValue = dval; } + + /** + * @brief Sets a single value at a specific index. + * @param dval the value to set + * @param idx the index at which to set the value + */ void SetValue(double dval, unsigned idx); + + /** + * @brief Sets all error values for this variable. + * @param dval vector of error values to assign + */ void SetError(std::vector &dval) { fError = dval; } + + /** + * @brief Sets a single error value at a specific index. + * @param dval the error value to set + * @param idx the index at which to set the error value + */ void SetError(double dval, unsigned idx); + /** + * @brief Gets the variable name. + * @return the name of this variable + */ std::string GetName() { return fName; } + + /** + * @brief Gets the size of the variable data. + * @return the size if value and error vectors match, 0 otherwise + */ unsigned int GetSize() { return (fValue.size() == fError.size()) ? fValue.size() : 0; } + + /** + * @brief Gets all values. + * @return vector of all values + */ std::vector GetValue() { return fValue; } + + /** + * @brief Gets a single value at a specific index. + * @param idx the index of the value to retrieve + * @return the value at the specified index, or 0 if index is out of range + */ double GetValue(unsigned int idx) { return (idx < fValue.size()) ? fValue[idx] : 0; } + + /** + * @brief Gets all error values. + * @return vector of all error values + */ std::vector GetError() { return fError; } + + /** + * @brief Gets a single error value at a specific index. + * @param idx the index of the error value to retrieve + * @return the error value at the specified index, or 0 if index is out of range + */ double GetError(unsigned int idx) { return (idx < fError.size()) ? fError[idx] : 0; } private: - std::string fName; - std::vector fValue; - std::vector fError; + std::string fName; ///< Variable name + std::vector fValue; ///< Vector of values (one per run) + std::vector fError; ///< Vector of error values (one per run) }; /////////////////////////////////////////////////////////////////////////// - // Program Semantic Analysis + /** + * @brief The PProgram struct performs semantic analysis on the AST. + * + * This visitor pattern struct traverses the AST generated by the parser + * and performs semantic checks: + * - Verifies that variables are declared before use + * - Prevents duplicate variable declarations + * - Validates function and operator usage + * - Resolves variable references and position-based lookups + * + * After successful semantic analysis, the variable table can be used by + * PProgEval for expression evaluation. Semantic errors are reported via + * the error handler with source position information. + */ /////////////////////////////////////////////////////////////////////////// struct PProgram { - typedef bool result_type; + typedef bool result_type; ///< Return type for all visitor methods + /** + * @brief Constructor that sets up the error handler. + * @tparam PErrorHandler the error handler type + * @param error_handler_ reference to the error handler for reporting semantic errors + */ template PProgram(PErrorHandler& error_handler_) { @@ -91,67 +180,278 @@ namespace mupp { namespace prog { "**ERROR** ", _2, phx::cref(error_handler_.iters)[_1]); } + /** + * @brief Visitor for nil AST nodes (should never be called). + * @return always false (assertion failure) + */ bool operator()(ast::nil) { BOOST_ASSERT(0); return false; } + + /** + * @brief Visitor for numeric literal nodes. + * @param x the numeric value + * @return true (literals are always valid) + */ bool operator()(double x); + + /** + * @brief Visitor for assignment statements. + * @param x the assignment AST node + * @return true if variable exists and RHS is valid, false otherwise + */ bool operator()(ast::assignment const &x); + + /** + * @brief Visitor for expression nodes. + * @param x the expression AST node + * @return true if all operands and operations are valid, false otherwise + */ bool operator()(ast::expression const &x); + + /** + * @brief Visitor for function call nodes. + * @param x the function AST node + * @return true if function is valid and argument is valid, false otherwise + */ bool operator()(ast::function const &x); + + /** + * @brief Visitor for binary operation nodes. + * @param x the operation AST node + * @return true if operation and operand are valid, false otherwise + */ bool operator()(ast::operation const &x); + + /** + * @brief Visitor for power operation nodes. + * @param x the power AST node + * @return true if base and exponent are valid, false otherwise + */ bool operator()(ast::power const &x); + + /** + * @brief Visitor for statement nodes (variant wrapper). + * @param x the statement AST node + * @return result of visiting the underlying statement type + */ bool operator()(ast::statement const &x); + + /** + * @brief Visitor for statement lists (program entry point). + * @param x the statement list AST node + * @return true if all statements are valid, false if any statement fails + */ bool operator()(ast::statement_list const &x); + + /** + * @brief Visitor for unary operation nodes. + * @param x the unary AST node + * @return true if operator and operand are valid, false otherwise + */ bool operator()(ast::unary const &x); + + /** + * @brief Visitor for variable reference nodes. + * @param x the variable AST node + * @return true if variable is declared, false otherwise + */ bool operator()(ast::variable const &x); + + /** + * @brief Visitor for variable declaration nodes. + * @param x the variable declaration AST node + * @return true if declaration is valid and RHS (if present) is valid, false otherwise + */ bool operator()(ast::variable_declaration const &x); + /** + * @brief Injects predefined variable values from collection data. + * @param name the variable name + * @param val vector of values for this variable + * @param err vector of error values for this variable + */ void add_predef_var_values(const std::string &name, std::vector &val, std::vector &err); + /** + * @brief Adds a variable to the symbol table. + * @param name the variable name to add + */ void add_var(std::string const& name); + + /** + * @brief Checks if a variable exists in the symbol table. + * @param name the variable name to find (with or without leading '$') + * @return true if variable exists, false otherwise + */ bool find_var(std::string const &name); + + /** + * @brief Finds a variable and returns its index. + * @param name the variable name to find (with or without leading '$') + * @param idx output parameter for the variable index + * @return true if variable exists, false otherwise + */ bool find_var(std::string const &name, unsigned int &idx); + + /** + * @brief Converts position-based variable reference to variable name. + * + * Supports syntax like $0, $1, etc. to reference variables by position. + * @param name the variable reference (name or position number) + * @param ok output parameter indicating whether conversion succeeded + * @return the resolved variable name, or "??" if position is out of range + */ std::string pos_to_var(std::string const &name, bool &ok); + /** + * @brief Gets all variables from the symbol table. + * @return vector of all variable handlers + */ std::vector getVars() { return fVariable; } private: - std::vector fVariable; - std::map fVarPos; + std::vector fVariable; ///< Symbol table of declared variables + std::map fVarPos; ///< Map from position index to variable name boost::function< void(int tag, std::string const& what)> - error_handler; + error_handler; ///< Error handler function for reporting semantic errors }; /////////////////////////////////////////////////////////////////////////// - // Program Evaluation + /** + * @brief The PProgEval struct evaluates expressions using the AST. + * + * This visitor pattern struct traverses the AST and computes numerical + * results for all variables. It operates on vector-valued variables where + * each vector element corresponds to a different run in the collection. + * + * The evaluator: + * - Evaluates arithmetic operations element-wise on vectors + * - Computes mathematical functions on each vector element + * - Handles both regular variables and error variables (suffixed with 'Err') + * - Stores results back into the variable table + * + * After evaluation, variable values and errors can be extracted using + * getVar() for use in the GUI. + */ /////////////////////////////////////////////////////////////////////////// struct PProgEval { - typedef std::vector result_type; + typedef std::vector result_type; ///< Return type for all visitor methods (vector of values) + /** + * @brief Constructor that initializes the evaluator with variables. + * @param var vector of variable handlers from semantic analysis + */ PProgEval(std::vector var) : fVariable(var) {} + /** + * @brief Evaluates a nil node (should never be called). + * @return zero-filled vector + */ std::vector operator()(ast::nil); + + /** + * @brief Evaluates a numeric literal. + * @param x the numeric value + * @return vector filled with the literal value + */ std::vector operator()(double x); + + /** + * @brief Evaluates an assignment statement. + * @param x the assignment AST node + * @return the computed RHS values, also stored in the variable + */ std::vector operator()(ast::assignment const &x); + + /** + * @brief Evaluates an expression. + * @param x the expression AST node + * @return vector of computed values + */ std::vector operator()(ast::expression const &x); + + /** + * @brief Evaluates a function call. + * @param x the function AST node + * @return vector of function results applied element-wise + */ std::vector operator()(ast::function const &x); + + /** + * @brief Evaluates a binary operation. + * @param x the operation AST node + * @param lhs vector of left-hand side values + * @return vector of operation results applied element-wise + */ std::vector operator()(ast::operation const &x, std::vector lhs); + + /** + * @brief Evaluates a power operation. + * @param x the power AST node + * @return vector of pow(base, exponent) computed element-wise + */ std::vector operator()(ast::power const &x); + + /** + * @brief Evaluates a statement. + * @param x the statement AST node + * @return zero-filled vector (statements have side effects only) + */ std::vector operator()(ast::statement const &x); + + /** + * @brief Evaluates a statement list (program entry point). + * @param x the statement list AST node + * @return zero-filled vector (evaluation has side effects on variables) + */ std::vector operator()(ast::statement_list const &x); + + /** + * @brief Evaluates a unary operation. + * @param x the unary AST node + * @return vector of unary operation results applied element-wise + */ std::vector operator()(ast::unary const &x); + + /** + * @brief Evaluates a variable reference. + * @param x the variable AST node + * @return vector of variable values (or errors for variables ending with 'Err') + */ std::vector operator()(ast::variable const &x); + + /** + * @brief Evaluates a variable declaration. + * @param x the variable declaration AST node + * @return vector of initialization values (or predefined values for injected variables) + */ std::vector operator()(ast::variable_declaration const &x); + /** + * @brief Retrieves a variable by name after evaluation. + * @param name the variable name to find + * @param ok output parameter indicating whether variable was found + * @return the variable handler, or the first variable if not found + */ PVarHandler getVar(const std::string name, bool &ok); + + /** + * @brief Prints all variable results to standard output (debugging). + */ void print_result(); private: - std::vector fVariable; + std::vector fVariable; ///< Variable table with values and errors + /** + * @brief Finds a variable index by name. + * @param name the variable name (with or without leading '$') + * @return the index of the variable, or 0 if not found + */ unsigned int find_var(std::string const &name); }; }} diff --git a/src/musredit_qt6/mupp/var/include/PSkipper.hpp b/src/musredit_qt6/mupp/var/include/PSkipper.hpp index 18af44f9e..cb357c40e 100644 --- a/src/musredit_qt6/mupp/var/include/PSkipper.hpp +++ b/src/musredit_qt6/mupp/var/include/PSkipper.hpp @@ -41,11 +41,38 @@ namespace mupp { namespace parser namespace ascii = boost::spirit::ascii; /////////////////////////////////////////////////////////////////////////////// - // The skipper grammar + /** + * @brief The PSkipper grammar for skipping whitespace and comments. + * + * This grammar defines what the parser should skip (ignore) between tokens. + * It handles: + * - Whitespace: spaces, tabs, carriage returns, line feeds + * - C-style block comments: /* comment * / + * - Single-line comments: % comment, # comment, // comment + * + * The skipper is used automatically by the parser between all grammar rules, + * allowing flexible formatting and documentation of input expressions. + * + * Example valid comments: + * @code + * var x = $y + 1.0 // this is a comment + * var z = $x * 2.0 # another comment + * var a = $z / 3.0 % yet another comment + * /* This is a + * multi-line comment * / + * @endcode + * + * @tparam Iterator the iterator type for the input (typically std::string::const_iterator) + */ /////////////////////////////////////////////////////////////////////////////// template struct PSkipper : qi::grammar { + /** + * @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_; @@ -63,8 +90,8 @@ namespace mupp { namespace parser ; } - qi::rule single_line_comment; - qi::rule start; + qi::rule single_line_comment; ///< Rule for single-line comments (%, #, //) + qi::rule start; ///< Top-level skipper rule }; }} diff --git a/src/musredit_qt6/mupp/var/include/PStatement.hpp b/src/musredit_qt6/mupp/var/include/PStatement.hpp index cc960d571..0208a4f55 100644 --- a/src/musredit_qt6/mupp/var/include/PStatement.hpp +++ b/src/musredit_qt6/mupp/var/include/PStatement.hpp @@ -38,18 +38,45 @@ namespace mupp { namespace parser { /////////////////////////////////////////////////////////////////////////////// - // The statement grammar + /** + * @brief The PStatement grammar for parsing variable statements. + * + * This grammar defines the syntax for variable declarations and assignments. + * It builds on top of the PExpression grammar to parse complete statements + * that form a variable definition program. + * + * Supported statement types: + * - Variable declaration: var = + * - Variable declaration without initialization: var + * - Assignment: = + * + * Example statements: + * @code + * var sigma = pow(abs(pow($T1,2.0)-pow(0.11,2.0)),0.5) + * var sigmaErr = 0.01 + * result = $sigma * 2.0 + * @endcode + * + * 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 struct PStatement : qi::grammar > { + /** + * @brief Constructor that initializes the statement grammar rules. + * @param error_handler reference to the error handler for reporting parse errors + */ PStatement(PErrorHandler& error_handler); - PExpression expr; - qi::rule > statement_list; - qi::rule > variable_declaration; - qi::rule > assignment; - qi::rule > identifier; + PExpression expr; ///< Expression grammar for parsing right-hand sides + qi::rule > statement_list; ///< Rule for parsing a list of statements + qi::rule > variable_declaration; ///< Rule for variable declarations + qi::rule > assignment; ///< Rule for assignment statements + qi::rule > identifier; ///< Rule for identifiers (without '$' prefix) }; }} diff --git a/src/musredit_qt6/mupp/var/include/PStatementDef.hpp b/src/musredit_qt6/mupp/var/include/PStatementDef.hpp index 6ba9b3eb8..caa770fea 100644 --- a/src/musredit_qt6/mupp/var/include/PStatementDef.hpp +++ b/src/musredit_qt6/mupp/var/include/PStatementDef.hpp @@ -8,6 +8,18 @@ Based on Joel de Guzman example on calc7, 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. + + The grammar supports: + - Variable declarations: var = + - Variable declarations without initialization: var + - Assignments: = + + The grammar integrates with PExpression for parsing right-hand side + expressions and includes error handling and AST annotation. + ***************************************************************************/ /*************************************************************************** diff --git a/src/musredit_qt6/mupp/var/include/PVarHandler.h b/src/musredit_qt6/mupp/var/include/PVarHandler.h index 8ee7cf956..8fa3c5018 100644 --- a/src/musredit_qt6/mupp/var/include/PVarHandler.h +++ b/src/musredit_qt6/mupp/var/include/PVarHandler.h @@ -39,30 +39,125 @@ #include "PAst.hpp" #include "PProgram.hpp" +//----------------------------------------------------------------------------- +/** + * @brief The PVarHandler class handles variable parsing, evaluation, and data management. + * + * This class provides the main interface for parsing variable definition strings, + * evaluating expressions using the Boost.Spirit parser framework, and managing + * the resulting computed values and errors. It integrates with PmuppCollection + * to access run data and inject predefined variables for use in expressions. + * + * The variable syntax supports: + * - Variable declarations: var = + * - Identifiers (prefixed with '$'): $varName + * - Mathematical functions: sin, cos, tan, exp, log, sqrt, pow, etc. + * - Arithmetic operations: +, -, *, / + * - Error variables (suffixed with 'Err'): must be defined for each variable + * + * Example usage: + * @code + * PmuppCollection *coll = ...; + * std::string expr = "var sigma = pow(abs(pow($T1,2.0)-pow(0.11,2.0)),0.5)"; + * PVarHandler handler(coll, expr, "sigma"); + * if (handler.isValid()) { + * std::vector values = handler.getValues(); + * std::vector errors = handler.getErrors(); + * } + * @endcode + */ class PVarHandler { public: + /** + * @brief Default constructor. + * + * Creates an invalid PVarHandler instance with null pointers and empty strings. + */ PVarHandler(); + + /** + * @brief Constructor that parses and evaluates a variable expression. + * + * Parses the provided expression string, performs semantic analysis, injects + * predefined variables from the collection, evaluates the expression, and + * stores the results. + * + * @param coll pointer to the PmuppCollection containing run data and parameters + * @param parse_str the variable definition string to parse (e.g., "var x = $T1 + 1.0") + * @param var_name optional variable name to extract from the evaluation results; if empty, only parsing/checking is performed + */ PVarHandler(PmuppCollection *coll, std::string parse_str, std::string var_name=""); + /** + * @brief Checks if the parsing and evaluation were successful. + * @return true if the expression was parsed and evaluated successfully, false otherwise + */ bool isValid() { return fIsValid; } + + /** + * @brief Gets the collection name. + * @return QString containing the name of the associated collection + */ QString getCollName() { return fColl->GetName(); } + + /** + * @brief Gets the variable name. + * @return QString containing the variable name + */ QString getVarName() { return QString(fVarName.c_str()); } + + /** + * @brief Gets the computed values for the variable. + * @return vector of double values computed from the expression evaluation + */ std::vector getValues(); + + /** + * @brief Gets the computed errors for the variable. + * @return vector of double error values computed from the expression evaluation + */ std::vector getErrors(); private: - PmuppCollection *fColl; ///< collection needed for parsing and evaluation - std::string fParseStr; ///< the variable input to be parsed - std::string fVarName; ///< variable name - mupp::prog::PVarHandler fVar; ///< values of the evaluation + PmuppCollection *fColl; ///< pointer to collection containing run data needed for parsing and evaluation + std::string fParseStr; ///< the variable input string to be parsed + std::string fVarName; ///< name of the variable to extract from evaluation results + mupp::prog::PVarHandler fVar; ///< variable handler storing the computed values and errors - bool fIsValid; - mupp::ast::statement_list fAst; ///< the AST + bool fIsValid; ///< flag indicating whether parsing and evaluation succeeded + mupp::ast::statement_list fAst; ///< Abstract Syntax Tree generated from parsing + /** + * @brief Injects predefined variables from the collection into the AST. + * + * Extracts all parameter names from the collection's first run and creates + * variable declarations for both the parameter names and their corresponding + * error variables (suffixed with 'Err'). These declarations are prepended + * to the AST before evaluation. + */ void injectPredefVariables(); + /** + * @brief Gets the variable name at a specific parameter index. + * @param idx the parameter index in the collection + * @return variable name as a string, or "??" if index is out of range + */ std::string getVarName(int idx); + + /** + * @brief Gets the data values for a specific parameter across all runs. + * @param idx the parameter index in the collection + * @return vector of data values from all runs for the specified parameter + */ std::vector getData(int idx); + + /** + * @brief Gets the error values for a specific parameter across all runs. + * + * Computes the geometric mean of positive and negative errors for each run. + * @param idx the parameter index in the collection + * @return vector of error values (geometric mean of pos/neg errors) from all runs + */ std::vector getDataErr(int idx); }; diff --git a/src/musredit_qt6/mupp/var/src/PExpression.cpp b/src/musredit_qt6/mupp/var/src/PExpression.cpp index 550ce1491..ca95e8535 100644 --- a/src/musredit_qt6/mupp/var/src/PExpression.cpp +++ b/src/musredit_qt6/mupp/var/src/PExpression.cpp @@ -1,6 +1,6 @@ /*************************************************************************** - PExpression.hpp + PExpression.cpp Author: Andreas Suter e-mail: andreas.suter@psi.ch @@ -8,6 +8,11 @@ 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 + compilation unit reduces compile times and allows the grammar + implementation to be hidden from clients. + ***************************************************************************/ /*************************************************************************** diff --git a/src/musredit_qt6/mupp/var/src/PProgram.cpp b/src/musredit_qt6/mupp/var/src/PProgram.cpp index f6fab459a..02f0704ba 100644 --- a/src/musredit_qt6/mupp/var/src/PProgram.cpp +++ b/src/musredit_qt6/mupp/var/src/PProgram.cpp @@ -8,6 +8,24 @@ Based on Joel de Guzman example on calc7, see https://github.com/boostorg/spirit + This file implements the semantic analysis (PProgram) and evaluation + (PProgEval) visitor classes for the variable expression AST. + + PProgram performs semantic checks: + - Variable declaration validation + - Variable reference resolution + - Type and operator validation + - Building the symbol table + + PProgEval performs expression evaluation: + - Element-wise vector operations + - Mathematical function evaluation + - Variable value and error computation + - Result storage in variable handlers + + Both classes use the visitor pattern to traverse the AST generated by + the parser and process each node type appropriately. + ***************************************************************************/ /*************************************************************************** diff --git a/src/musredit_qt6/mupp/var/src/PStatement.cpp b/src/musredit_qt6/mupp/var/src/PStatement.cpp index e1662cdba..1f815640e 100644 --- a/src/musredit_qt6/mupp/var/src/PStatement.cpp +++ b/src/musredit_qt6/mupp/var/src/PStatement.cpp @@ -8,6 +8,11 @@ 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 + compilation unit reduces compile times and allows the grammar + implementation to be hidden from clients. + ***************************************************************************/ /*************************************************************************** diff --git a/src/musredit_qt6/mupp/var/src/PVarHandler.cpp b/src/musredit_qt6/mupp/var/src/PVarHandler.cpp index 505fa937a..889eb23ff 100644 --- a/src/musredit_qt6/mupp/var/src/PVarHandler.cpp +++ b/src/musredit_qt6/mupp/var/src/PVarHandler.cpp @@ -38,7 +38,10 @@ //-------------------------------------------------------------------------- /** - * @brief PVarHandler::PVarHandler + * @brief Default constructor creating an invalid handler. + * + * Initializes all members to default/null values, resulting in an invalid + * handler that will return false from isValid(). */ PVarHandler::PVarHandler() : fColl(nullptr), fParseStr(""), fVarName(""), fIsValid(false) @@ -48,7 +51,22 @@ PVarHandler::PVarHandler() : //-------------------------------------------------------------------------- /** - * @brief PVarHandler::PVarHandler + * @brief Constructor that parses and evaluates a variable expression. + * + * Performs the complete variable processing pipeline: + * 1. Injects predefined variables from the collection into the AST + * 2. Parses the input string using the PStatement grammar + * 3. Performs semantic analysis using PProgram + * 4. Injects actual data values from the collection + * 5. Evaluates the expression using PProgEval + * 6. Extracts and stores the requested variable results + * + * If parsing, semantic analysis, or evaluation fails, fIsValid is set to false + * and error messages are logged to ~/.musrfit/mupp/mupp_err.log. + * + * @param coll pointer to the collection containing run data + * @param parse_str the variable definition string to parse + * @param var_name optional variable name to extract; if empty, only validation is performed */ PVarHandler::PVarHandler(PmuppCollection *coll, std::string parse_str, std::string var_name) : fColl(coll), fParseStr(parse_str), fVarName(var_name), fIsValid(false) @@ -95,8 +113,12 @@ PVarHandler::PVarHandler(PmuppCollection *coll, std::string parse_str, std::stri //-------------------------------------------------------------------------- /** - * @brief PVarHandler::getValues - * @return + * @brief Gets the computed values for the variable. + * + * Returns the vector of values computed during expression evaluation. + * Each element corresponds to a different run in the collection. + * + * @return vector of computed values, or empty vector if not valid */ std::vector PVarHandler::getValues() { @@ -109,8 +131,12 @@ std::vector PVarHandler::getValues() //-------------------------------------------------------------------------- /** - * @brief PVarHandler::getErrors - * @return + * @brief Gets the computed error values for the variable. + * + * Returns the vector of error values computed during expression evaluation. + * Each element corresponds to a different run in the collection. + * + * @return vector of computed error values, or empty vector if not valid */ std::vector PVarHandler::getErrors() { @@ -123,7 +149,13 @@ std::vector PVarHandler::getErrors() //-------------------------------------------------------------------------- /** - * @brief PVarHandler::injectPredefVariables + * @brief Injects predefined variables from the collection into the AST. + * + * For each parameter in the collection's first run, creates variable + * declarations for both the parameter name and its corresponding error + * variable (with 'Err' suffix). These declarations are prepended to the + * AST so they are processed before user-defined variables, making all + * collection parameters available for use in expressions. */ void PVarHandler::injectPredefVariables() { @@ -148,9 +180,13 @@ void PVarHandler::injectPredefVariables() //-------------------------------------------------------------------------- /** - * @brief PVarHandler::getVarName - * @param idx - * @return + * @brief Gets the variable name at a specific parameter index. + * + * Retrieves the parameter name from the collection's first run at the + * specified index. + * + * @param idx the parameter index (0-based) + * @return the parameter name, or "??" if index is out of range */ std::string PVarHandler::getVarName(int idx) { @@ -165,9 +201,13 @@ std::string PVarHandler::getVarName(int idx) //-------------------------------------------------------------------------- /** - * @brief PVarHandler::getData - * @param idx - * @return + * @brief Gets the data values for a specific parameter across all runs. + * + * Collects the value of the specified parameter from every run in the + * collection, returning them as a vector. + * + * @param idx the parameter index (0-based) + * @return vector of parameter values from all runs, or empty vector if index is out of range */ std::vector PVarHandler::getData(int idx) { @@ -188,9 +228,14 @@ std::vector PVarHandler::getData(int idx) //-------------------------------------------------------------------------- /** - * @brief PVarHandler::getDataErr - * @param idx - * @return + * @brief Gets the error values for a specific parameter across all runs. + * + * Collects the error of the specified parameter from every run in the + * collection. The error is computed as the geometric mean of the positive + * and negative errors: sqrt(abs(posErr * negErr)). + * + * @param idx the parameter index (0-based) + * @return vector of parameter errors from all runs, or empty vector if index is out of range */ std::vector PVarHandler::getDataErr(int idx) {