Skip to content

Commit

Permalink
Bug fix: Now fix parsing of categories more than 2 indent levels prior
Browse files Browse the repository at this point in the history
It was discovered that this YAML markup was not being parsed properly
in qfyaml 0.3.2:

   operations:
     transport:
       passive_species:
         CH3ITracer:
           long_name: Methyl_iodide
           mol_wt_in_g: 142.0
           lifetime_in_s: 4.32e5
           default_bkg_conc_in_vv: 1.0e-20
     wet_deposition:
       activate: true

The wet_deposition category was given the wrong indentation depth,
and as such, operations:wet_deposition:activate was parsed as FALSE
but should have been TRUE.

This issue is now fixed and we will release this as qfyaml 0.3.0.

NOTE: QFYAML works best if a constant indentation (e.g. always 2 spaces
for each level) is used.  You can use an editor such as Emacs to format
your YAML input files accordingly.

Also updated the test programs to test this behavior.

Signed-off-by: Bob Yantosca <yantosca@seas.harvard.edu>
  • Loading branch information
yantosca committed Feb 25, 2022
1 parent cf41507 commit d3cdbdb
Show file tree
Hide file tree
Showing 5 changed files with 129 additions and 31 deletions.
30 changes: 30 additions & 0 deletions docs/source/known-bugs.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,36 @@ This page lists known bugs in :program:`qfyaml`. See the `Github
issues <http:s://github.com/yantosca/qfyaml/issues>`_ page for updates
on their status.

*************
Version 0.3.2
*************

Error parsing categories
========================

We discovered an error parsing this YAML file, where the
:code:`wet_deposition` tag is more than 2 indentation levels behind
behind the previous line.

.. code-block:: yaml
operations:
transport:
passive_species:
CH3ITracer:
long_name: Methyl_iodide
mol_wt_in_g: 142.0
lifetime_in_s: 4.32e5
default_bkg_conc_in_vv: 1.0e-20
wet_deposition:
activate: true
This has now been fixed in qfyaml 0.3.3.

NOTE: For best results with qfyaml, we recommend formatting YAML files
so that they contain a consistent indentation level throughout the
file (i.e. such as 2 or 4 spaces). Editors such as Emacs can do this easily.

*************
Version 0.3.0
*************
Expand Down
72 changes: 61 additions & 11 deletions src/qfyaml_mod.F90
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ MODULE QFYAML_Mod
PUBLIC :: QFYAML_CleanUp
PUBLIC :: QFYAML_Get
PUBLIC :: QFYAML_Check
PUBLIC :: QFYAML_FindDepth
PUBLIC :: QFYAML_FindNextHigher
PUBLIC :: QFYAML_Init
PUBLIC :: QFYAML_Merge
Expand Down Expand Up @@ -741,7 +742,8 @@ SUBROUTINE Parse_Line( yml, yml_anchored, set_by, &
! SAVEd variables
LOGICAL, SAVE :: is_list_var = .FALSE.
INTEGER, SAVE :: last_pos = 0
INTEGER, SAVE :: cat_pos(20) = 0
INTEGER, SAVE :: indent = 0
INTEGER, SAVE :: cat_pos(QFYAML_MaxStack) = 0
INTEGER, SAVE :: cat_index = 0
CHARACTER(LEN=QFYAML_NamLen), SAVE :: cat_stack(QFYAML_MaxStack) = ""

Expand Down Expand Up @@ -805,23 +807,27 @@ SUBROUTINE Parse_Line( yml, yml_anchored, set_by, &
! If this category starts further along the line than the last
! category, then increment index and add its position to the stack.
IF ( pos > last_pos ) THEN
indent = last_pos - pos
cat_index = cat_index + 1
cat_pos(cat_index) = pos
ENDIF

! If this category starts earlier along the line than the last
! category, then decrement index and add its position to the stack.
! last category, then this
! NOTE: This algorithm will work best if we assume a constant
! indentation level. Best to use an editor such as emacs
! to enforce a consistent indentation throughout the file.
IF ( pos < last_pos ) THEN
cat_index = cat_index - 1
indent = last_pos - pos
cat_index = cat_index - ( indent / 2 )
cat_pos(cat_index) = pos
ENDIF

! If the index is negative or the category begins at the first
! character of the line, then set index to 1 and store its
! starting position in the first element of the stack.
IF ( cat_index <= 0 .or. pos == 1 ) THEN
cat_index = 1
cat_index = 1
cat_pos(cat_index) = pos
ENDIF

Expand Down Expand Up @@ -927,9 +933,9 @@ SUBROUTINE Parse_Line( yml, yml_anchored, set_by, &
category = cat_stack(C)
IF ( C > 1 ) THEN
DO CC = C-1, 1, -1
category = TRIM( cat_stack(CC) ) // &
QFYAML_Category_Separator // &
TRIM( category )
category = TRIM( cat_stack(CC) ) // &
QFYAML_Category_Separator // &
TRIM( category )
ENDDO
ENDIF
EXIT
Expand All @@ -941,16 +947,15 @@ SUBROUTINE Parse_Line( yml, yml_anchored, set_by, &
category = cat_stack( MAX( C-1, 1 ) )
IF ( C-1 > 1 ) THEN
DO CC = C-2, 1, -1
category = TRIM( cat_stack(CC) ) // &
QFYAML_Category_Separator // &
TRIM( category )
category = TRIM( cat_stack(CC) ) // &
QFYAML_Category_Separator // &
TRIM( category )
ENDDO
ENDIF
EXIT
ENDIF
ENDDO


! Test if the variable is a YAML anchor
IF ( var_name == "<<" ) THEN

Expand Down Expand Up @@ -1498,6 +1503,51 @@ END SUBROUTINE QFYAML_check
!
! !IROUTINE: QFYAML_FindNextHigher
!
! !DESCRIPTION: For a given category or variable name, returns its depth
! (i.e. indentation level). This is equal to the number of separator
! strings.
!\\
!\\
! !INTERFACE:
!
FUNCTION QFYAML_FindDepth( name ) RESULT( depth )
!
! !INPUT PARAMETERS:
!
CHARACTER(LEN=*), INTENT(IN) :: name
!
! RETURN VALUE:
!
INTEGER :: depth
!EOP
!------------------------------------------------------------------------------
!BOC
!
! !LOCAL VARIABLES:
!
INTEGER :: ix, c

! Keep searching for all category separators
! until there aren't any more.
depth = 1
c = LEN_TRIM( name )
DO
ix = INDEX( name(1:c), QFYAML_Category_Separator, back=.TRUE. )
IF ( ix <= 1 ) EXIT
depth = depth + 1
c = ix - 1
ENDDO

END FUNCTION QFYAML_FindDepth
!EOC
!------------------------------------------------------------------------------
! QFYAML: Bob Yantosca | yantosca@seas.harvard.edu | Apr 2020
! Based on existing package https://github.com/jannisteunissen/config_fortran
!------------------------------------------------------------------------------
!BOP
!
! !IROUTINE: QFYAML_FindNextHigher
!
! !DESCRIPTION: Finds variables that are one category depth higher than
! a given target string (trg_str). Returns the number of variables that
! match this criteria (n_matches), as well as the variables themselves
Expand Down
17 changes: 15 additions & 2 deletions test/qfyaml.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
#### input.yml - Input for test_qfyaml.F90
#### qfyaml.yml - Input for test_qfyaml.F90

#### Test going back & forth between levels
weather:
Expand All @@ -10,7 +10,18 @@ weather:
units: K
pressure: 1013.25

#### Test parsing different types of input
operations:
transport:
passive_species:
CH3ITracer:
long_name: Methyl_iodide
mol_wt_in_g: 142.0
lifetime_in_s: 4.32e5
default_bkg_conc_in_vv: 1.0e-20
wet_deposition:
activate: true

##### Test parsing different types of input
author:
age: 29
fav_reals: [1.0, 2.0]
Expand Down Expand Up @@ -47,3 +58,5 @@ filename: another_file
author_name:
first: Homer
full: Homer J. Simpson


33 changes: 19 additions & 14 deletions test/test_qfyaml.F90
Original file line number Diff line number Diff line change
Expand Up @@ -65,63 +65,68 @@ PROGRAM Test_QFYAML

key = "author%age"
v_int = -999
CALL QFYAML_Add_Get( yml, key, v_int, "", RC )
CALL QFYAML_Add_Get( yml, TRIM( key ), v_int, "", RC )
WRITE( 6, "(a32, "" : "", i7)") ADJUSTL(key), v_int

key = "author%fav_reals"
ALLOCATE( a_real(2) )
a_real = -999.0_yp
CALL QFYAML_Add_Get( yml, key, a_real, "", RC )
CALL QFYAML_Add_Get( yml, TRIM( key ), a_real, "", RC )
WRITE( 6, "(a32, "" : "", 2f7.2)") ADJUSTL(key), a_real
DEALLOCATE( a_real )

key = "author%more_reals"
ALLOCATE( a_real(4) )
a_real = -999.0_yp
CALL QFYAML_Add_Get( yml, key, a_real, "", RC )
CALL QFYAML_Add_Get( yml, TRIM( key ), a_real, "", RC )
WRITE( 6, "(a32, "" : "", 4f11.6)") ADJUSTL(key), a_real
DEALLOCATE( a_real )

key = "author%lots_of_work"
v_bool = .FALSE.
CALL QFYAML_Add_Get( yml, key, v_bool, "", RC )
CALL QFYAML_Add_Get( yml, TRIM( key ), v_bool, "", RC )
WRITE( 6, "(a32, "" : "", l7)") ADJUSTL(key), v_bool

key = "author_name%first"
v_str = ""
CALL QFYAML_Add_Get( yml, key, v_str, "", RC )
CALL QFYAML_Add_Get( yml, TRIM( key ), v_str, "", RC )
WRITE( 6, "(a32, "" : "", a)") ADJUSTL(key), TRIM(v_str)

key = "author_name%full"
v_str = ""
CALL QFYAML_Add_Get( yml, key, v_str, "", RC )
CALL QFYAML_Add_Get( yml, TRIM( key ), v_str, "", RC )
WRITE( 6, "(a32, "" : "", a)") ADJUSTL(key), TRIM(v_str)

key = "filename"
v_str = ""
CALL QFYAML_Add_Get( yml, key, v_str, "", RC )
CALL QFYAML_Add_Get( yml, TRIM( key ), v_str, "", RC )
WRITE( 6, "(a32, "" : "", a)") ADJUSTL(key), TRIM(v_str)

key = "weather%humidity"
v_real = -999.0_yp
CALL QFYAML_Add_Get( yml, key, v_real, "", RC )
CALL QFYAML_Add_Get( yml, TRIM( key ), v_real, "", RC )
WRITE( 6, "(a32, "" : "", f13.6)") ADJUSTL(key), v_real

key = "weather%temperature%daily"
v_real = -999.0_yp
CALL QFYAML_Add_Get( yml, key, v_real, "", RC )
CALL QFYAML_Add_Get( yml, TRIM( key ), v_real, "", RC )
WRITE( 6, "(a32, "" : "", f13.6)") ADJUSTL(key), v_real

key = "weather%temperature%weekly%units"
v_real = -999.0_yp
CALL QFYAML_Add_Get( yml, key, v_str, "", RC )
CALL QFYAML_Add_Get( yml, TRIM( key ), v_str, "", RC )
WRITE( 6, "(a32, "" : "", a)") ADJUSTL(key), TRIM(v_str)

key = "weather%pressure"
v_real = -999.0_yp
CALL QFYAML_Add_Get( yml, key, v_real, "", RC )
CALL QFYAML_Add_Get( yml, TRIM( key ), v_real, "", RC )
WRITE( 6, "(a32, "" : "", f13.6)") ADJUSTL(key), v_real

key = "operations%wet_deposition%activate"
v_bool = .FALSE.
CALL QFYAML_Add_Get( yml, TRIM( key ), v_bool, "", RC )
WRITE( 6, "(a32, "" : "", l7)") ADJUSTL(key), v_bool

WRITE( 6, '(/, a)' ) '### FIND NEXT-HIGHER VARIABLES IN "weather"'

CALL QFYAML_FindNextHigher( yml, "weather%", match_ct, match_vars )
Expand All @@ -135,7 +140,7 @@ PROGRAM Test_QFYAML
key = "fruits"
ALLOCATE( a_str(3) )
a_str = ""
CALL QFYAML_Add_Get( yml, key, a_str, "", RC )
CALL QFYAML_Add_Get( yml, TRIM( key ), a_str, "", RC )
WRITE( 6, "(a)" ) TRIM(key)
DO N = 1, SIZE( a_str )
print*, N, TRIM( a_str(N) )
Expand All @@ -146,7 +151,7 @@ PROGRAM Test_QFYAML
key = "more_fruits%p_fruits"
ALLOCATE( a_str(4) )
a_str = ""
CALL QFYAML_Add_Get( yml, key, a_str, "", RC )
CALL QFYAML_Add_Get( yml, TRIM( key ), a_str, "", RC )
WRITE( 6, "(a)" ) TRIM(key)
DO N = 1, SIZE( a_str )
print*, N, TRIM( a_str(N) )
Expand All @@ -158,7 +163,7 @@ PROGRAM Test_QFYAML
key = "even_more_fruits%exotic_fruits%hard_to_find"
ALLOCATE( a_str(5) )
a_str = ""
CALL QFYAML_Add_Get( yml, key, a_str, "", RC )
CALL QFYAML_Add_Get( yml, TRIM( key ), a_str, "", RC )
WRITE( 6, "(a)" ) TRIM(key)
DO N = 1, SIZE( a_str )
print*, N, TRIM( a_str(N) )
Expand Down
8 changes: 4 additions & 4 deletions test/test_species_database.F90
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ PROGRAM Test_Species_Database

! Strings
CHARACTER(LEN=14) :: tag
CHARACTER(LEN=14) :: spc
CHARACTER(LEN=31) :: spc
CHARACTER(LEN=255) :: v_str
CHARACTER(LEN=255) :: key
CHARACTER(LEN=255) :: fileName
Expand All @@ -44,7 +44,7 @@ PROGRAM Test_Species_Database

! String arrays
CHARACTER(LEN=17) :: tags(46)
CHARACTER(LEN=14) :: species(11)
CHARACTER(LEN=31) :: species(11)

! Objects
TYPE(QFYAML_t) :: yml
Expand Down Expand Up @@ -77,8 +77,8 @@ PROGRAM Test_Species_Database

RC = QFYAML_SUCCESS
mw_g = MISSING_INT
species(1) = "ACTA"
species(2) = "ALD2"
species(1) = "CO"
species(2) = "COAnthroEmis25dayTracer"
species(3) = "ALK4"
species(4) = "ASOA1"
species(5) = "ASOA2"
Expand Down

0 comments on commit d3cdbdb

Please sign in to comment.