• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

trixi-framework / HOHQMesh / 30318764346

28 Jul 2026 12:55AM UTC coverage: 76.972% (+1.1%) from 75.909%
30318764346

Pull #166

github

web-flow
Merge 3346edfd3 into 955f92dca
Pull Request #166: Add L2/H1 boundary optimization

1631 of 1964 new or added lines in 40 files covered. (83.04%)

48 existing lines in 4 files now uncovered.

9563 of 12424 relevant lines covered (76.97%)

634449.32 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

89.86
/Source/IO/SMScanner.f90
1
!
2
!////////////////////////////////////////////////////////////////////////
3
!
4
!>      SMScanner.f90
5
!>      Created: March 23, 2026 at 8:34 AM
6
!>      By: David Kopriva
7
!>
8
!>      A basic scanner type
9
!>
10
!>      Usage:
11
!>
12
!>         Initialization
13
!>            self % initWithString(str, delims)
14
!>
15
!>               str    is the string to be scanned
16
!>               delims is a string of the delimiters, e.g. "[,]" to delimit
17
!>                      any of the characters [ ] and ,
18
!>
19
!>               On initialization the cursor is over the first character of str
20
!>
21
!>            self % reset()
22
!>
23
!>               Returns the cursor to the first position
24
!>
25
!>         Scanning
26
!>
27
!>            done = scanUptoString(self, str)
28
!>
29
!>               Moves the cursor to just after the string "str"
30
!>               done is .TRUE. if the end of the string is reached
31
!>
32
!>            i   = self % scanInt()
33
!>
34
!>                Scans from the cursor position up to the next delimiter
35
!>                and attempts to convert str(currentPos:delimPos) to an
36
!>                integer. If that fails, the value NOT_AN_INTEGER is
37
!>                returned. Cursor is placed after the delimiter.
38
!>
39
!>            r   = self % scanReal()
40
!>
41
!>                Scans from the cursor position up to the next delimiter
42
!>                and attempts to convert str(currentPos:delimPos) to a
43
!>                real. If that fails, NaN is returned.
44
!>                Cursor is placed after the delimiter.
45
!>
46
!>         Example
47
!>
48
!>            CHARACTER(LEN = 12) :: str  = "[124, 2.718]"
49
!>            CHARACTER(LEN=3)    :: dlms = "[],"
50
!>            CALL scanner % initWithString(str,dlms)
51
!>            done       = scanner % scanUpToString("[")
52
!>            intResult  = scanner % scanInt()  ! = 124
53
!>            realResult = scanner % scanReal() ! = 2.178
54
!>
55
!
56
!////////////////////////////////////////////////////////////////////////
57
!
58
   Module SMScannerClass
59
   USE, INTRINSIC :: iso_fortran_env, only : stderr => ERROR_UNIT
60
   USE IEEE_ARITHMETIC
61
   IMPLICIT NONE
62

63
   TYPE SMScanner
64
      CHARACTER(LEN=:), ALLOCATABLE :: str
65
      CHARACTER(LEN=:), ALLOCATABLE :: delimiters   ! String with the delimiters
66
      CHARACTER(LEN=1)              :: delm         ! Last delimiter scanned
67
      INTEGER                       :: cursorPos
68
      INTEGER                       :: endPos
69
!
70
!     ========
71
      CONTAINS
72
!     ========
73
!
74
      PROCEDURE :: initWithString
75
      PROCEDURE :: reset
76
      PROCEDURE :: scanUpToString
77
      PROCEDURE :: scanInt
78
      PROCEDURE :: scanReal
79
      PROCEDURE :: isAtEnd
80
      PROCEDURE :: scanIntsToArray
81
      PROCEDURE :: lastDelimiter
82
   END TYPE SMScanner
83

84
   INTEGER, PARAMETER :: NOT_AN_INTEGER = -HUGE(NOT_AN_INTEGER)
85
!
86
!  ========
87
   CONTAINS
88
!  ========
89
!
90
!
91
!////////////////////////////////////////////////////////////////////////
92
!
93
   SUBROUTINE initWithString(self, str, delims)
9✔
94
      IMPLICIT NONE
95
      CLASS(SMScanner)  :: self
96
      CHARACTER(LEN=*)  :: str
97
      CHARACTER(LEN=*)  :: delims
98

99
      CALL DestructScanner(self)
9✔
100
      self % str        = str
9✔
101
      self % delimiters = delims
9✔
102
      self % cursorPos  = 1
9✔
103
      self % endPos     = LEN_TRIM(str)
9✔
104
      self % delm       = ""
9✔
105

106
   END SUBROUTINE initWithString
9✔
107
!
108
!////////////////////////////////////////////////////////////////////////
109
!
110
   SUBROUTINE DestructScanner(self)
9✔
111
      IMPLICIT NONE
112
      CLASS(SMScanner)  :: self
113
      self % cursorPos  = 1
9✔
114
      IF(ALLOCATED(self % str)) DEALLOCATE(self % str)
9✔
115
      IF(ALLOCATED(self % delimiters)) DEALLOCATE(self % delimiters)
9✔
116
   END SUBROUTINE DestructScanner
9✔
117
!
118
!////////////////////////////////////////////////////////////////////////
119
!
NEW
120
   SUBROUTINE reset(self)
×
121
      IMPLICIT NONE
122
      CLASS(SMScanner)  :: self
NEW
123
      self % cursorPos  = 1
×
NEW
124
   END SUBROUTINE reset
×
125
!
126
!////////////////////////////////////////////////////////////////////////
127
!
128
   LOGICAL FUNCTION isAtEnd(self)
12✔
129
      IMPLICIT NONE
130
      CLASS(SMScanner)  :: self
131
      isAtEnd = .FALSE.
12✔
132
      IF(self % cursorPos > self % endPos) isAtEnd = .TRUE.
12✔
133
   END FUNCTION isAtEnd
12✔
134
!
135
!////////////////////////////////////////////////////////////////////////
136
!
137
   LOGICAL FUNCTION scanUptoString(self, str)
8✔
138
!
139
!  --------------------------------------------------
140
!  Scan up to the string str and position the cursor
141
!  just after it. Returns true if the whole string
142
!  has been scanned, otherwise false.
143
!  --------------------------------------------------
144
!
145
      IMPLICIT NONE
146
!
147
!     ---------
148
!     Arguments
149
!     ---------
150
!
151
      CLASS(SMScanner) :: self
152
      CHARACTER(LEN=*) :: str
153
!
154
!     ---------------
155
!     Local variables
156
!     ---------------
157
!
158
      INTEGER :: k
159

160
      scanUptoString   = .FALSE.
8✔
161
      k                = INDEX(self % str(self % cursorPos: self % endPos), &
162
                               SUBSTRING = str)
8✔
163
      IF ( k == 0 )     THEN
8✔
164
         scanUptoString = .TRUE.
1✔
165
         RETURN
1✔
166
      END IF
167

168
      self % cursorPos = self % cursorPos + k + LEN_TRIM(str) - 1
7✔
169

170
   END FUNCTION scanUptoString
8✔
171
!
172
!////////////////////////////////////////////////////////////////////////
173
!
174
   SUBROUTINE getToken(self, token)
27✔
175
      IMPLICIT NONE
176
!
177
!     ---------
178
!     Arguments
179
!     ---------
180
!
181
      CLASS(SMScanner) :: self
182
      CHARACTER(LEN=*) :: token
183
!
184
!     ---------------
185
!     Local variables
186
!     ---------------
187
!
188
      INTEGER :: scEnd, p, e
189

190
      p     = self % cursorPos
27✔
191
      e     = self % endPos
27✔
192
!
193
      scEnd = SCAN(STRING = self % str(p:e), &
194
                   SET    = self % delimiters)
27✔
195

196
      IF(scEnd == 0) THEN
27✔
197
         scEnd       = self % endPos
6✔
198
         self % delm = ""
6✔
199
      ELSE
200
         self % delm = self % str(p+scEnd-1:p+scEnd-1)
21✔
201
         scEnd       = p + scEnd - 2
21✔
202
      END IF
203

204
      token = self % str(p:scEnd)
27✔
205
      self % cursorPos = scEnd + 2
27✔
206

207
   END SUBROUTINE getToken
27✔
208
!
209
!////////////////////////////////////////////////////////////////////////
210
!
211
   CHARACTER(LEN=1) FUNCTION lastDelimiter(self)
14✔
212
      IMPLICIT NONE
213
      CLASS(SMScanner) :: self
214

215
      lastDelimiter = self % delm
14✔
216

217
   END FUNCTION lastDelimiter
14✔
218
!
219
!////////////////////////////////////////////////////////////////////////
220
!
221
   FUNCTION scanInt(self) RESULT(intResult)
24✔
222
      IMPLICIT NONE
223
!
224
!     ---------
225
!     Arguments
226
!     ---------
227
!
228
      CLASS(SMScanner) :: self
229
      INTEGER          :: intResult
230
!
231
!     ---------------
232
!     Local variables
233
!     ---------------
234
!
235
      INTEGER            :: iErr
236
      CHARACTER(LEN=128) :: token
237

238
      CALL getToken(self,token)
24✔
239
      READ(  token, *, IOSTAT = iErr ) intResult
24✔
240
      IF ( iErr /=0 )     THEN
24✔
241
         intResult = -HUGE(intResult)
2✔
242
      END IF
243

244
   END FUNCTION scanInt
24✔
245
!
246
!////////////////////////////////////////////////////////////////////////
247
!
248
   FUNCTION scanReal(self) RESULT(res)
3✔
249
      IMPLICIT NONE
250
!
251
!     ---------
252
!     Arguments
253
!     ---------
254
!
255
      CLASS(SMScanner)  :: self
256
      REAL(KIND(1.0d0)) :: res
257
!
258
!     ---------------
259
!     Local variables
260
!     ---------------
261
!
262
      INTEGER            :: iErr
263
      CHARACTER(LEN=128) :: token
264

265
      CALL getToken(self,token)
3✔
266
      READ(  token, *, IOSTAT = iErr ) res
3✔
267

268
      IF ( iErr /=0 )     THEN
3✔
269
         res = IEEE_VALUE(res, IEEE_QUIET_NAN)
1✔
270
      END IF
271

272
   END FUNCTION scanReal
3✔
273
!
274
!////////////////////////////////////////////////////////////////////////
275
!
276
   FUNCTION scanIntsToArray(self)  RESULT(array)
2✔
277
!
278
!  ---------------------------------------------------------------
279
!  Scan through the string collecting integer values into an array
280
!  On error, the array is of zero size.
281
!  ---------------------------------------------------------------
282
!
283
      IMPLICIT NONE
284
!
285
!     ---------
286
!     Arguments
287
!     ---------
288
!
289
      CLASS(SMScanner)     :: self
290
      INTEGER, ALLOCATABLE :: array(:)
291
!
292
!     ---------------
293
!     Local variables
294
!     ---------------
295
!
296
      INTEGER :: j
297

298
      ALLOCATE(array(0))
2✔
299
      j = self % scanInt()
2✔
300

301
      IF ( j == NOT_AN_INTEGER ) RETURN
2✔
302
      array = [j]
2✔
303

304
      DO WHILE(.NOT. self % isAtEnd())
5✔
305
         j = self % scanInt()
4✔
306

307
         IF ( j == NOT_AN_INTEGER )     THEN
4✔
NEW
308
            DEALLOCATE(array)
×
NEW
309
            ALLOCATE(array(0))
×
NEW
310
            RETURN
×
311
         ELSE
312
            array = [array, j]
42✔
313
         END IF
314
      END DO
315

316
   END FUNCTION scanIntsToArray
317
!
318
!  ------------------------------------------------------------------------
319
!  Testing...
320
!  ------------------------------------------------------------------------
321
!
322
   LOGICAL FUNCTION scanUpToTest()
1✔
323
      IMPLICIT NONE
324
!                                      123456789012345
325
      CHARACTER(LEN = 64)   :: str = "[124, 3.14, ss]"
326
      INTEGER, DIMENSION(3) :: p = [2,6,12]
327
      CHARACTER(LEN= 3)   :: dlms = ",[]"
328
      TYPE(SMScanner)     :: scanner
1✔
329
      LOGICAL             :: done
330

331
      scanUpToTest = .TRUE.
1✔
332

333
      CALL scanner % initWithString(str,dlms)
1✔
334
      done = scanner % scanUpToString("[")
1✔
335
      IF(done) scanUpToTest = .FALSE.
1✔
336
      scanUpToTest = scanUpToTest .AND. scanner % cursorPos == p(1)
1✔
337

338
      done         = scanner % scanUpToString(",")
1✔
339
      scanUpToTest = scanUpToTest .AND. .NOT. done
1✔
340
      scanUpToTest = scanUpToTest .AND. scanner % cursorPos == p(2)
1✔
341

342
      done         = scanner % scanUpToString(",")
1✔
343
      scanUpToTest = scanUpToTest .AND. .NOT. done
1✔
344
      scanUpToTest = scanUpToTest .AND. scanner % cursorPos == p(3)
1✔
345

346
      done         = scanner % scanUpToString(",")
1✔
347
      scanUpToTest = scanUpToTest .AND. done
1✔
348

349
   END FUNCTION scanUpToTest
1✔
350
!
351
!////////////////////////////////////////////////////////////////////////
352
!
353
   LOGICAL FUNCTION scanTest()
1✔
354
      IMPLICIT NONE
355
!                                   12345678901234567890123456
356
      CHARACTER(LEN = 64) :: str  = "[124, 14, 3.14, 11, 2.718]"
357
      CHARACTER(LEN = 64) :: str2 = "[1x4, 3.z4 ]"
358
      CHARACTER(LEN= 3)   :: dlms = ",[]"
359
      TYPE(SMScanner)     :: scanner
1✔
360
      LOGICAL             :: done
361
      INTEGER             :: intResult
362
      REAL(KIND(1.0d0))   :: realResult
363

364
      CALL scanner % initWithString(str,dlms)
1✔
365
      done     = scanner % scanUpToString("[")
1✔
366
      scanTest = .NOT.done
1✔
367
!
368
!     --------
369
!     Scan 124
370
!     --------
371
!
372
      intResult = scanner % scanInt()
1✔
373
      IF(intResult == NOT_AN_INTEGER)     THEN
1✔
NEW
374
         scanTest = .FALSE.
×
375
      ELSE
376
         scanTest = scanTest .AND. intResult == 124
1✔
377
      END IF
378
!
379
!     -------
380
!     Scan 14
381
!     -------
382
!
383
      intResult = scanner % scanInt()
1✔
384
      IF(intResult == NOT_AN_INTEGER)     THEN
1✔
NEW
385
         scanTest = .FALSE.
×
386
      ELSE
387
         scanTest = scanTest .AND. intResult == 14
1✔
388
      END IF
389
!
390
!     ---------
391
!     Scan 3.14
392
!     ---------
393
!
394
      realResult = scanner % scanReal()
1✔
395

396
      IF(IEEE_IS_NAN (realResult))     THEN
1✔
NEW
397
         scanTest = .FALSE.
×
398
      ELSE
399
         scanTest = scanTest .AND. ABS(realResult - 3.14d0) < 1.0d-9
1✔
400
      END IF
401
!
402
!     -------
403
!     Scan 11
404
!     -------
405
!
406
      intResult = scanner % scanInt()
1✔
407
      IF(intResult == NOT_AN_INTEGER)     THEN
1✔
NEW
408
         scanTest = .FALSE.
×
409
      ELSE
410
         scanTest = scanTest .AND. intResult == 11
1✔
411
      END IF
412
!
413
!     ----------
414
!     Scan 2.718
415
!     ----------
416
!
417
      realResult = scanner % scanReal()
1✔
418

419
      IF(IEEE_IS_NAN (realResult))     THEN
1✔
NEW
420
         scanTest = .FALSE.
×
421
      ELSE
422
         scanTest = scanTest .AND. ABS(realResult - 2.718d0) < 1.0d-9
1✔
423
      END IF
424
!
425
!     --------
426
!     Failures
427
!     --------
428
!
429
      CALL scanner % initWithString(str2,dlms)
1✔
430
      done     = scanner % scanUpToString("[")
1✔
431
      scanTest = .NOT.done
1✔
432
!
433
!     --------
434
!     Scan 1x4
435
!     --------
436
!
437
      intResult = scanner % scanInt()
1✔
438
      IF(intResult == NOT_AN_INTEGER)     THEN
1✔
439
         scanTest = scanTest .AND. .TRUE.
1✔
440
      ELSE
NEW
441
         scanTest = .FALSE.
×
442
      END IF
443
!
444
!     ---------
445
!     Scan 3.z4
446
!     ---------
447
!
448
      realResult = scanner % scanReal()
1✔
449

450
      IF(IEEE_IS_NAN (realResult))     THEN
1✔
451
         scanTest = scanTest .AND. .TRUE.
1✔
452
      ELSE
NEW
453
         scanTest = .FALSE.
×
454
      END IF
455

456
   END FUNCTION scanTest
1✔
457
!
458
!////////////////////////////////////////////////////////////////////////
459
!
460
   LOGICAL FUNCTION scanArrayTest()
1✔
461
      IMPLICIT NONE
462
!                                         123456789012345678901234567
463
      CHARACTER(LEN = 64)  :: str      = " breaks = 124, 14,3, 11,2"
464
      CHARACTER(LEN = 64)  :: failStr  = " breaks = xxy"
465
      CHARACTER(LEN= 1)    :: dlms = ","
466
      TYPE(SMScanner)      :: scanner
1✔
467
      LOGICAL              :: done
468
      INTEGER              :: c(5) = [124,14,3,11,2]
469
      INTEGER, ALLOCATABLE :: a(:)
1✔
470
!
471
!     ------------
472
!     Test success
473
!     ------------
474
!
475
      CALL scanner % initWithString(str,dlms)
1✔
476
      done     = scanner % scanUpToString("=")
1✔
477
      scanArrayTest = .NOT.done
1✔
478
      IF(done) RETURN
1✔
479

480
      a = scanIntsToArray(scanner)
6✔
481
      scanArrayTest = scanArrayTest .AND. MAXVAL(a-c) == 0
6✔
482
      DEALLOCATE(a)
1✔
483
!
484
!     ------------
485
!     Test failure
486
!     ------------
487
!
488
      CALL scanner % initWithString(failStr,dlms)
1✔
489
      done     = scanner % scanUpToString("=")
1✔
490
      scanArrayTest = scanArrayTest .AND. .NOT.done
1✔
491
      IF(done) RETURN
1✔
492
      a = scanIntsToArray(scanner)
1✔
493
      scanArrayTest = scanArrayTest .AND. SIZE(a) == 0
1✔
494

495
   END FUNCTION scanArrayTest
1✔
496

NEW
497
   END Module SMScannerClass
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc