From 745a5647e05e211a33aa42592fa8f20c36816762 Mon Sep 17 00:00:00 2001 From: Joe Schoonover Date: Sun, 5 Oct 2025 15:15:18 -0400 Subject: [PATCH 1/3] [#44] Add reproducer for reported parsing error --- test/CMakeLists.txt | 3 +- test/parsing_simple_r1fp32.f90 | 75 ++++++++++++++++++++++++++++++++++ 2 files changed, 77 insertions(+), 1 deletion(-) create mode 100644 test/parsing_simple_r1fp32.f90 diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index dd26d4f..20e432e 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -219,6 +219,7 @@ add_fortran_tests ( "print_tokens.f90" "custom_r1fp32.f90" "custom_r1fp64.f90" + "parsing_simple_r1fp32.f90" "parsing_difficult_r1fp64.f90" "parsing_vanderpol_sfp64.f90") - \ No newline at end of file + diff --git a/test/parsing_simple_r1fp32.f90 b/test/parsing_simple_r1fp32.f90 new file mode 100644 index 0000000..543ec64 --- /dev/null +++ b/test/parsing_simple_r1fp32.f90 @@ -0,0 +1,75 @@ +! //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ! +! +! Maintainers : support@fluidnumerics.com +! Official Repository : https://github.com/FluidNumerics/feq-parse/ +! +! Copyright © 2024 Fluid Numerics LLC +! +! Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: +! +! 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. +! +! 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in +! the documentation and/or other materials provided with the distribution. +! +! 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from +! this software without specific prior written permission. +! +! THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +! LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +! HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +! LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +! THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +! THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +! +! //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ! + +program test + + implicit none + integer :: exit_code + + exit_code = parsing_simple_r1fp32() + stop exit_code + +contains + + integer function parsing_simple_r1fp32() result(r) + use FEQParse + use iso_fortran_env + implicit none + integer,parameter :: N = 10 + type(EquationParser) :: f + character(LEN=1),dimension(1) :: independentVars + character(LEN=2048) :: eqChar + real(real32) :: x(1:N,1) + real(real32) :: feval(1:N) + real(real32) :: fexact(1:N) + integer :: i + + ! Specify the independent variables + independentVars = (/'x'/) + + ! Specify an equation string that we want to evaluate + eqChar = 'f = x-sqrt(x) + 100' + + ! Create the EquationParser object + f = EquationParser(eqChar,independentVars) + + x = 0.0_real32 + do i = 1,N + x(i,1) = (2.0_real32)/real(N,real32)*real(i-1,real32) + fexact(i) = x(i,1) - sqrt(x(i,1)) + 100.0 + enddo + + ! Evaluate the equation + feval = f%evaluate(x) + if(maxval(abs(feval-fexact)) <= epsilon(1.0_real32)) then + r = 0 + else + r = 1 + endif + + endfunction parsing_simple_r1fp32 + +endprogram test From b3ee1416db63b868f0c362069fce1ccba557a611 Mon Sep 17 00:00:00 2001 From: Joe Schoonover Date: Mon, 26 Jan 2026 15:10:00 -0500 Subject: [PATCH 2/3] [#44] Fix shunting-yard algorithm for function calls and operator precedence Fix two bugs in ConvertToPostFix that caused incorrect parsing of expressions like 'x-sqrt(x)+100': 1. Pop function token after closing parenthesis - the standard shunting-yard algorithm requires popping the function to output after the opening parenthesis is removed 2. Handle left-associative operator precedence - operators +, -, *, / are left-associative and should pop equal-precedence operators, while ^ is right-associative and should not Co-Authored-By: Claude Opus 4.5 --- src/FEQParse.f90 | 16 ++++++++++++++-- test/parsing_simple_r1fp32.f90 | 5 ++++- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/src/FEQParse.f90 b/src/FEQParse.f90 index e429d88..8e02c74 100644 --- a/src/FEQParse.f90 +++ b/src/FEQParse.f90 @@ -377,9 +377,12 @@ subroutine ConvertToPostFix(parser) tok = operator_stack%TopToken() + ! Pop operators with higher priority, or equal priority for left-associative ops + ! (all operators except '^' are left-associative) do while(trim(tok%tokenString) /= '(' .and. & - parser%Priority(tok) > & - parser%Priority(parser%inFix%tokens(i)) .and. & + (parser%Priority(tok) > parser%Priority(parser%inFix%tokens(i)) .or. & + (parser%Priority(tok) == parser%Priority(parser%inFix%tokens(i)) .and. & + trim(parser%inFix%tokens(i)%tokenString) /= '^')) .and. & .not. operator_stack%IsEmpty()) call parser%postFix%push(tok) @@ -411,6 +414,15 @@ subroutine ConvertToPostFix(parser) ! Pop the opening parenthesis call operator_stack%pop(tok) + ! If there is a function token at the top of the operator stack, pop it to output + if(.not. operator_stack%IsEmpty()) then + tok = operator_stack%TopToken() + if(tok%tokenType == Function_Token) then + call parser%postFix%push(tok) + call operator_stack%pop(tok) + endif + endif + endif enddo diff --git a/test/parsing_simple_r1fp32.f90 b/test/parsing_simple_r1fp32.f90 index 543ec64..dbbf2ef 100644 --- a/test/parsing_simple_r1fp32.f90 +++ b/test/parsing_simple_r1fp32.f90 @@ -59,7 +59,7 @@ integer function parsing_simple_r1fp32() result(r) x = 0.0_real32 do i = 1,N x(i,1) = (2.0_real32)/real(N,real32)*real(i-1,real32) - fexact(i) = x(i,1) - sqrt(x(i,1)) + 100.0 + fexact(i) = x(i,1)-sqrt(x(i,1))+100.0 enddo ! Evaluate the equation @@ -67,6 +67,9 @@ integer function parsing_simple_r1fp32() result(r) if(maxval(abs(feval-fexact)) <= epsilon(1.0_real32)) then r = 0 else + do i = 1,N + print*,x(i,1),feval(i),fexact(i),abs(feval(i)-fexact(i)) + enddo r = 1 endif From dd2436e3f10cad3e9eef0fd588ef30cc0f010184 Mon Sep 17 00:00:00 2001 From: Joe Schoonover Date: Mon, 26 Jan 2026 15:11:43 -0500 Subject: [PATCH 3/3] Add file for AI agent assistance --- CLAUDE.md | 201 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 201 insertions(+) create mode 100644 CLAUDE.md diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..a3cde1d --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,201 @@ +# AGENTS.md +AI Agent Operating Rules for This Repository + +This file defines **hard constraints** and **preferred behaviors** for AI coding +agents (e.g., Claude, Codex) working in this repository. These rules exist to protect +numerical correctness, performance, and long-term maintainability of a +a dynamic equation parser Fortran library codebase. + +Agents MUST follow all rules below unless explicitly instructed otherwise by a +human maintainer. + +--- + +## 1. Project Purpose + +This project implements and infix tokenizer, infix to postfix conversion utilities, and postfix calculator +that works with scalars and higher rank arrays in Fortran for dynamic equation parsing and evaluation in Fortran. + +Primary goals: +- Users should be able to provide valid infix equations as strings and evaluate them reliably without error +- Numerical correctness +- High performance on CPUs + +Changes must NOT degrade: +- Correctness + +--- + +## 2. Fortran Standards & Toolchain + +### Language Standard +- Target: **Fortran 2008** +- Code must remain compatible with: + - gfortran ≥ 11 + - ifx + - nvfortran + - amdflang + +### Prohibited Language Features +Do NOT introduce: +- Coarrays +- Fortran 2018+ features +- Compiler-specific extensions +- Automatic polymorphism in performance-critical paths + +### Formatting & Conventions +- Free-form source +- `implicit none` required in all program units +- Explicit `intent(in|out|inout)` on all dummy arguments +- Lowercase keywords preferred +- Line length ≤ 100 characters + +--- + +## 3. Numerical & Algorithmic Constraints + +### Hard Rules +- Do NOT change the mathematical formulation without approval +- Do NOT change discretization order +- Do NOT change basis, quadrature, or nodal ordering +- Do NOT reorder floating-point reductions +- Do NOT alter time integration schemes + +### Floating-Point Behavior +- Bitwise reproducibility may be required +- Preserve operation ordering in loops +- Avoid algebraic “simplifications” unless mathematically justified + +### Array Semantics +- Do NOT replace explicit loops with array syntax unless equivalence is proven +- Avoid implicit temporaries + +--- + +## 4. Performance Rules (Critical) + +This is an HPC codebase. Performance regressions are unacceptable. + +### Memory +- Avoid temporary allocations in hot paths +- No automatic arrays in tight loops +- No hidden allocations via array slicing + +### Loops +- Preserve loop ordering for cache locality +- Do NOT replace DO loops with WHERE / FORALL +- Vectorization-friendly structure must be preserved + +### Abstraction +- Do NOT introduce runtime polymorphism in kernels +- Avoid excessive modularization inside hot loops + +--- + +## 5. Code Organization & APIs + +### File & Module Structure +- Do NOT rename modules +- Do NOT move files between directories +- Do NOT change public interfaces without approval +- Preserve module dependency order + +### Public APIs +- Public procedures are considered **stable** +- Backward compatibility is required unless stated otherwise + +--- + +## 6. Testing & Validation + +### Required +- All existing regression tests must pass +- Do NOT modify reference output files +- Numerical differences must be justified + +### New Code +- New features require: + - A test case + - Clear validation criteria + +--- + +## 7. Documentation & Comments + +### Preserve Scientific Meaning +- Do NOT remove comments describing: + - Equations + - Algorithms + - Numerical assumptions + +### New Routines +Must include: +- Mathematical description +- Variable meaning and units +- Expected input ranges + +--- + +## 8. Prohibited Actions (Explicit) + +Agents MUST NOT: +- Rewrite code in another language +- Convert procedural code to OO Fortran +- Replace MPI with coarrays +- Introduce external dependencies +- “Modernize” syntax without benefit +- Delete legacy code without explanation + +--- + +## 10. Preferred Agent Behavior + +Agents SHOULD: +- Ask before changing algorithms +- Explain numerical and performance implications +- Provide minimal diffs +- Reference existing patterns in the codebase +- Flag any uncertainty explicitly + +Agents SHOULD NOT: +- Make large refactors unless requested +- Assume intent beyond the explicit request + +--- + +## 11. When in Doubt + +If a change could affect: +- Numerical accuracy +- Stability +- Performance +- Parallel behavior + +STOP and ask for clarification. + +--- +## 12. Local Dev Commands (Required) + +Agents MUST use the commands below for formatting and tests. +If these commands fail due to missing Docker, agents should not invent alternatives. + +### Lint / Format (Fortran) +Run fprettify on the repository: + +```bash +./scripts/lint.sh +``` +To apply formatting changes: +``` +./scripts/format.sh +``` + +### Unit/Regression tests + +``` +./scripts/test.sh +``` +--- + +End of AGENTS.md +