Loading...
Searching...
No Matches
parser.f90
Go to the documentation of this file.
1!> @file
2!! @defgroup group_parser Parser
3!! Fortran Preprocessor (fpx) - core parsing and preprocessing module
4!!
5!! This module implements a full-featured, modern Fortran preprocessor supporting:
6!! - C-style line continuations with `\` and `\\`
7!! - Fortran-style `&` continuations
8!! - `#define`, `#undef`, object-like and function-like macros with variadic support
9!! - `#include` with proper path resolution and recursion guard
10!! - Conditional compilation: `#if`, `#ifdef`, `#ifndef`, `#elif`, `#else`, `#endif`
11!! - Non-standard `#for` directive
12!! - C-style `/* ... */` comments (nestable aware)
13!! - Macro expansion with argument substitution and stringification (`#`) / token-pasting (`##`)
14!! - Interactive REPL mode when reading from stdin
15!! - Multiple entry points for file-to-file, unit-to-unit, etc.
16!! - Support ${x} for substituting macro name
17!!
18!! The preprocessor is designed to be standards-conforming where possible while adding
19!! useful extensions (variadic macros, better diagnostics, include path handling).
20!!
21!! @par Processing Pipeline
22!! Source files are processed in several stages:
23!!
24!! 1. Input acquisition from files, units, or stdin
25!! 2. C-style continuation handling (`\`, `\\`)
26!! 3. Fortran continuation handling (`&`)
27!! 4. C-style block comment removal
28!! 5. Directive recognition and execution
29!! 6. Conditional compilation evaluation
30!! 7. Macro expansion
31!! 8. Emission to the output stream
32!!
33!! Include files recursively invoke the same processing pipeline.
34!!
35!! @par Parser State
36!! The parser maintains module-level state describing:
37!! - current source file,
38!! - line continuation status,
39!! - comment state,
40!! - deferred reprocessing buffers,
41!! - interactive mode output state.
42!!
43!! This state is reset at the beginning of each top-level preprocessing run.
44!!
45!! The parser coordinates several specialized subsystems:
46!!
47!! - @ref group_define for macro definition directives,
48!! - @ref group_include for include processing,
49!! - @ref group_macro for macro expansion,
50!! - @ref group_conditional for conditional compilation,
51!! - @ref group_for for non-standard loop directives,
52!! - @ref group_diagnostics for reporting.
53!!
54!! @par fpx Extensions
55!! In addition to standard preprocessing facilities, fpx provides:
56!! - `#for` / `#endfor`
57!! - `${NAME}` macro insertion
58!! - implicit continuation support
59!! - interactive REPL mode
60!! - enhanced diagnostics
61!! @section parser_examples Examples
62!!
63!! 1. Preprocess a file to stdout:
64!! @code{.f90}
65!! call preprocess('input.F90')
66!! @endcode
67!!
68!! 2. Preprocess a file and write to another file:
69!! @code{.f90}
70!! call preprocess('src/main.F90', 'preprocessed/main.F90')
71!! @endcode
72!!
73!! 3. Use in a build system with unit numbers:
74!! @code{.f90}
75!! integer :: iu, ou
76!! open(newunit=iu, file='input.F90')
77!! open(newunit=ou, file='output.F90')
78!! call preprocess(iu, ou)
79!! close(iu); close(ou)
80!! ...
81!! @endcode
82!!
83!! 4. Interactive mode (stdin to stdout):
84!! @code
85!! $ ./fpx
86!! [in] #define PI 3.1415926535
87!! [out]
88!! [in] real :: x = PI*2
89!! [out] real :: x = 3.1415926535*2
90!! [in] (empty line or 'quit' to exit)
91!! @endcode
92module fpx_parser
93 use, intrinsic :: iso_fortran_env, only: stdout => output_unit, iostat_end, stdin => input_unit
94 use, intrinsic :: iso_c_binding, only: c_char, c_size_t, c_ptr, c_null_ptr, c_associated, c_funloc
95 use fpx_constants
96 use fpx_string
97 use fpx_logging
98 use fpx_macro
99 use fpx_conditional
100 use fpx_define
101 use fpx_diagnostics
102 use fpx_include
103 use fpx_path
104 use fpx_global
105 use fpx_context
106 use fpx_line
107 use fpx_for
108
109 implicit none; private
110
111 public :: preprocess, &
112 global
113
114 !> Generic interface to start preprocessing from various sources/sinks
115 !!
116 !! Allows preprocessing:
117 !! - file to stdout
118 !! - file to file
119 !! - unit to file
120 !! - unit to unit (most flexible, used internally for #include)
121 !!
122 !! @section preprocess_overloads Overloads
123 !!
124 !! @code{.f90}preprocess(character(*))@endcode
125 !! Preprocess a source file and write to stdout.
126 !!
127 !! @code{.f90}preprocess(character(*), character(*))@endcode
128 !! Preprocess a source file and write to another file.
129 !!
130 !! @code{.f90}preprocess(integer, integer)@endcode
131 !! Preprocess an already-open input unit to an output unit.
132 !!
133 !! @code{.f90}preprocess(integer, character(*))@endcode
134 !! Preprocess an already-open input unit to a file.
135 !!
136 !! @ingroup group_parser
137 interface preprocess
138 module procedure :: preprocess_file
139 module procedure :: preprocess_file_to_unit
140 module procedure :: preprocess_unit_to_file
141 module procedure :: preprocess_unit_to_unit
142 end interface
143
144 character(256) :: name !< Current source file name (without path)
145 logical :: c_continue !< Flags for C-style continuation
146 logical :: f_continue !< Flags for Fortran-style continuation
147 logical :: in_comment !< Internal state flags
148 logical :: reprocess !< Internal state flags
149 logical :: stitch !< Internal state flags
150 character(:), allocatable :: res !< Accumulated result line buffers
151 character(:), allocatable :: tmp !< Accumulated temporary line buffers
152 character(MAX_LINE_LEN) :: line !< Raw input line
153 character(MAX_LINE_LEN) :: continued_line !< Raw and continued input line
154 integer :: iline !< Current line number position
155 integer :: icontinuation !< Continuation position
156
157contains
158
159 !> Preprocess a file and write result to an optional output file (default: stdout)
160 !! Opens the input file, determines the base filename for error messages,
161 !! opens the output file if requested, and delegates to the unit-to-unit routine.
162 !! @param[in] filepath Path to the input source file
163 !! @param[in] outputfile Optional path to the output file; if absent output goes to stdout
164 !!
165 !! @ingroup group_parser
166 subroutine preprocess_file(filepath, outputfile)
167 character(*), intent(in) :: filepath
168 character(*), intent(in), optional :: outputfile
169 !private
170 integer :: iunit, ierr, n, ounit
171 character(len=1, kind=c_char) :: buf(256)
172
173 open(newunit=iunit, file=filepath, status='old', action='read', iostat=ierr)
174
175 if (ierr /= 0) then
176 call printf(render(diagnostic_report(level_error, &
177 message='Error opening input file: ' // trim(filepath), &
178 source=name), &
179 ''))
180 return
181 else
182 if (c_associated(getcwd_c(buf, size(buf, kind=c_size_t)))) then
183 n = findloc(buf, achar(0), 1)
184 name = filepath(n + 1:)
185 end if
186 end if
187
188 if (present(outputfile)) then
189 open(newunit=ounit, file=outputfile, status='replace', action='write', iostat=ierr)
190 if (ierr /= 0) then
191 call printf(render(diagnostic_report(level_error, &
192 message='Error opening input file: ' // trim(outputfile), &
193 source=name), &
194 ''))
195 close(iunit)
196 return
197 end if
198 else
199 ounit = stdout
200 end if
201
202 call preprocess(iunit, ounit)
203 if (iunit /= stdin) close(iunit)
204 if (ounit /= stdout) close(ounit)
205 end subroutine
206
207 !> Preprocess from an already-open input unit and write to a file
208 !! @param[in] iunit Input unit (must already be open for reading)
209 !! @param[in] ofile Output filename
210 !!
211 !! @ingroup group_parser
212 subroutine preprocess_unit_to_file(iunit, ofile)
213 integer, intent(in) :: iunit
214 character(*), intent(in) :: ofile
215 !private
216 integer :: ierr, ounit
217
218 if (iunit /= stdin) then
219 inquire(unit = iunit, name=name)
220 end if
221
222 open(newunit=ounit, file=ofile, status='replace', action='write', iostat=ierr)
223 if (ierr /= 0) then
224 call printf(render(diagnostic_report(level_error, &
225 message='Error opening input file: ' // trim(ofile), &
226 source=name), &
227 ''))
228 close(iunit)
229 return
230 end if
231
232 call preprocess(iunit, ounit)
233 if (iunit /= stdin) close(iunit)
234 if (ounit /= stdout) close(ounit)
235 end subroutine
236
237 !> Preprocess a file and write to an already-open output unit
238 !! @param[in] ifile Input filename
239 !! @param[in] ounit Output unit (already open for writing)
240 !!
241 !! @ingroup group_parser
242 subroutine preprocess_file_to_unit(ifile, ounit)
243 character(*), intent(in) :: ifile
244 integer, intent(in) :: ounit
245 !private
246 integer :: iunit, ierr, n
247 character(len=1, kind=c_char) :: buf(256)
248
249 open(newunit=iunit, file=ifile, status='old', action='read', iostat=ierr)
250 if (ierr /= 0) then
251 call printf(render(diagnostic_report(level_error, &
252 message='Error opening input file: ' // trim(ifile), &
253 source=name), &
254 ''))
255 return
256 else
257 if (c_associated(getcwd_c(buf, size(buf, kind=c_size_t)))) then
258 n = findloc(buf, achar(0), 1)
259 name = ifile(n + 1:)
260 end if
261 end if
262
263 call preprocess(iunit, ounit)
264 if (iunit /= stdin) close(iunit)
265 if (ounit /= stdout) close(ounit)
266 end subroutine
267
268 !> Core preprocessing routine: read from iunit, write to ounit
269 !! Sets up a clean macro environment for the top-level file,
270 !! resets conditional compilation state, and calls the worker routine.
271 !!
272 !! A local copy of the global macro table is created so that
273 !! preprocessing sessions remain isolated while preserving
274 !! command-line definitions.
275 !!
276 !! @param[in] iunit Input unit
277 !! @param[in] ounit Output unit
278 !!
279 !! @ingroup group_parser
280 subroutine preprocess_unit_to_unit(iunit, ounit)
281 integer, intent(in) :: iunit
282 integer, intent(in) :: ounit
283 !private
284 type(macro), allocatable :: macros(:)
285
286 if (.not. allocated(global%macros)) allocate(global%macros(0))
287 allocate(macros(size_of(global%macros)), source=global%macros)
288 if (.not. allocated(global%undef)) allocate(global%undef(0))
289 if (.not. allocated(global%includedir)) allocate(global%includedir(0))
290
291 cond_depth = 0
292 cond_stack(1)%active = .true.
293 cond_stack(1)%has_met = .false.
294
295 reprocess = .false.; c_continue = .false.; f_continue = .false.
296 icontinuation = 1; iline = 0
297 continued_line = ''; res = ''
298
299 call preprocess_unit(iunit, ounit, macros, .false.)
300 deallocate(macros)
301 end subroutine
302
303 !> Worker routine that reads lines, handles continuations, comments and directives
304 !! @par Main Loop
305 !! The routine repeatedly:
306 !! - reads a physical line,
307 !! - merges continuations,
308 !! - processes directives,
309 !! - performs macro expansion,
310 !! - handles deferred Fortran continuation stitching,
311 !! - emits output.
312 !!
313 !! @param[in] iunit Input unit
314 !! @param[in] ounit Output unit
315 !! @param[inout] macros(:) Current macro table (passed by value between include levels)
316 !! @param[in] from_include True if called recursively from #include
317 !!
318 !! @ingroup group_parser
319 subroutine preprocess_unit(iunit, ounit, macros, from_include)
320 integer, intent(in) :: iunit
321 integer, intent(in) :: ounit
322 type(macro), allocatable, intent(inout) :: macros(:)
323 logical, intent(in) :: from_include
324 !private
325 integer :: ierr, n
326
327 do
328 if (global%interactive) write(*, '(/a)', advance='no') ' [in] ' ! Command line prompt
329 read(iunit, '(A)', iostat=ierr) line
330
331 if (global%interactive) then
332 if (line == '') exit
333 if (lowercase(trim(adjustl(line))) == 'quit') exit
334 end if
335 if (ierr /= 0) then
336 if (ierr == iostat_end .and. from_include) f_continue = tail(tmp) == '&'
337 exit
338 end if
339 if (.not. from_include) iline = iline + 1
340
341 if (c_continue) then
342 continued_line = continued_line(:icontinuation) // trim(adjustl(line))
343 else
344 continued_line = trim(adjustl(line))
345 end if
346 n = len_trim(continued_line); if (n == 0) cycle
347
348 ! Check for line continuation with '\'
349 if (verify(continued_line(n:n), '\') == 0) then
350 ! Check for line break with '\\'
351 if (continued_line(len_trim(continued_line) - 1:len_trim(continued_line)) == '\\' .and. global%line_break) then
352 c_continue = .true.
353 continued_line = continued_line(:len_trim(continued_line) - 2) // new_line('A') ! Strip '\\'
354 icontinuation = len_trim(continued_line)
355 else
356 c_continue = .true.
357 icontinuation = len_trim(continued_line) - 1
358 continued_line = continued_line(:icontinuation)
359 end if
360 cycle
361 else
362 c_continue = .false.
363
364 tmp = process_line(continued_line, ounit, name, iline, macros, stitch)
365 if (len_trim(tmp) == 0) cycle
366
367 in_comment = head(tmp) == '!'
368
369 if (merge(head(res) == '!', in_comment, len_trim(res) > 0)) then
370 f_continue = tail(tmp) == '&'
371 else
372 if (in_comment .and. f_continue) cycle
373 f_continue = .not. in_comment .and. tail(tmp) == '&'
374 end if
375
376 if ((.not. global%disable_continuation) .and. (f_continue .or. stitch)) then
377 reprocess = .true.
378 res = concat(res, tmp)
379 else
380 if (reprocess) then
381 if (.not. in_comment .and. head(res) == '!') then
382 if (is_in_forloop()) then
383 call add_to_loop(res)
384 else
385 write(ounit, '(A)') res
386 end if
387 res = process_line(tmp, ounit, name, iline, macros, stitch)
388 else
389 res = process_line(concat(res, tmp), ounit, name, iline, macros, stitch)
390 end if
391 reprocess = .false.
392 else
393 res = trim(tmp)
394 end if
395
396 if (is_in_forloop()) then
397 call add_to_loop(res)
398 else
399 if (global%interactive) write(*, '(/a)', advance='no') ' [out] ' ! Command line prompt
400 write(ounit, '(A)') res
401 end if
402 res = ''
403 end if
404 end if
405 end do
406
407 if (cond_depth > 0) then
408 call printf(render(diagnostic_report(level_error, &
409 message='Unclosed conditional block at end of file', &
410 source=name), &
411 trim(line), iline))
412 else if (c_continue) then
413 call printf(render(diagnostic_report(level_error, &
414 message='Unexpected character', &
415 label=label_type('Trailing new line "\"', len(trim(line)), 1), &
416 source=name), &
417 trim(line), iline))
418 end if
419 end subroutine
420
421 !> Process a single (possibly continued) line - handles directives and macro expansion
422 !! Responsibilities:
423 !! - Strip or terminate C-style block comments (`/* ... */`)
424 !! - Detect and delegate preprocessor directives (`#define`, `#include`, conditionals, etc.)
425 !! - Perform macro expansion when the line is in an active conditional block
426 !! - Return whether the next line should be stitched (for Fortran `&` continuation inside macros)
427 !! This routine acts as the dispatcher for all preprocessing
428 !! directives and ordinary source lines.
429 !!
430 !! Directives are interpreted immediately, whereas ordinary
431 !! lines undergo macro expansion only when the current
432 !! conditional compilation state is active.
433 !! @param[in] current_line Input line (already continued and trimmed)
434 !! @param[in] ounit Output unit (used only for diagnostics inside called routines)
435 !! @param[in] filepath Current file name (for error messages)
436 !! @param[in] linenum Current line number (for error messages)
437 !! @param[inout] macros(:) Macro table
438 !! @param[out] stch Set to .true. if the expanded line ends with `&` (stitch next line)
439 !! @return Processed line (directives removed, macros expanded)
440 !!
441 !! @see
442 !! @link fpx_macro::expand_all expand_all @endlink
443 !! @link fpx_define::handle_define handle_define @endlink
444 !! @link fpx_include::handle_include handle_include @endlink
445 !!
446 !! @ingroup group_parser
447 recursive function process_line(current_line, ounit, filepath, linenum, macros, stch) result(rst)
448 character(*), intent(in) :: current_line
449 integer, intent(in) :: ounit
450 character(*), intent(inout) :: filepath
451 integer, intent(inout) :: linenum
452 type(macro), allocatable, intent(inout) :: macros(:)
453 logical, intent(out) :: stch
454 character(:), allocatable :: rst
455 !private
456 character(:), allocatable :: trimmed_line
457 logical :: active
458 logical, save :: l_in_comment = .false., l_in_loop = .false.
459 integer :: idx, comment_start, comment_end, n
460 type(context) :: ctx
461
462 trimmed_line = trim(adjustl(current_line))
463 rst = ''
464 comment_end = index(trimmed_line, '*/')
465 if (l_in_comment .and. comment_end > 0) then
466 trimmed_line = trimmed_line(comment_end + 2:)
467 l_in_comment = .false.
468 end if
469
470 if (l_in_comment) return
471 comment_start = index(trimmed_line, '/*')
472 if (comment_start > 0) then
473 trimmed_line = trimmed_line(:comment_start - 1)
474 l_in_comment = comment_end == 0
475 end if
476 n = len(trimmed_line); if (n == 0) return
477
478 active = is_active()
479 ctx = context(trimmed_line, linenum, filepath)
480 if (head(trimmed_line) == '#') then
481 if (len(trimmed_line) == 1) then
482 return !null directive
483 else if (starts_with(lowercase(adjustl(trimmed_line(2:))), 'for')) then
484 l_in_loop = .true.
485 if (global%support_forloop) call handle_for(ctx, macros, 'for')
486 else if (starts_with(lowercase(adjustl(trimmed_line(2:))), 'endfor')) then
487 l_in_loop = .false.
488 if (global%support_forloop) call handle_endfor(ctx, ounit, c_funloc(process_line), macros, 'endfor')
489 l_in_loop = is_in_forloop()
490 else if (l_in_loop) then
491 rst = trimmed_line
492 else if (starts_with(lowercase(adjustl(trimmed_line(2:))), 'define') .and. active) then
493 call handle_define(ctx, macros, 'define')
494 else if (starts_with(lowercase(adjustl(trimmed_line(2:))), 'undef') .and. active) then
495 call handle_undef(ctx, macros, 'undef')
496 else if (starts_with(lowercase(adjustl(trimmed_line(2:))), 'warning') .and. active) then
497 call handle_warning(ctx, macros, 'warning')
498 else if (starts_with(lowercase(adjustl(trimmed_line(2:))), 'error') .and. active) then
499 call handle_error(ctx, macros, 'error')
500 else if (starts_with(lowercase(adjustl(trimmed_line(2:))), 'include') .and. active) then
501 call handle_include(ctx, ounit, preprocess_unit, macros, 'include')
502 else if (starts_with(lowercase(adjustl(trimmed_line(2:))), 'line')) then
503 call handle_line(ctx, 'line')
504 else if (starts_with(lowercase(adjustl(trimmed_line(2:))), 'ifdef')) then
505 call handle_ifdef(ctx, macros, 'ifdef')
506 else if (starts_with(lowercase(adjustl(trimmed_line(2:))), 'ifndef')) then
507 call handle_ifndef(ctx, macros, 'ifndef')
508 else if (starts_with(lowercase(adjustl(trimmed_line(2:))), 'elifdef')) then
509 call handle_elifdef(ctx, macros, 'elifdef')
510 else if (starts_with(lowercase(adjustl(trimmed_line(2:))), 'elifndef')) then
511 call handle_elifndef(ctx, macros, 'elifndef')
512 else if (starts_with(lowercase(adjustl(trimmed_line(2:))), 'if')) then
513 call handle_if(ctx, macros, 'if')
514 else if (starts_with(lowercase(adjustl(trimmed_line(2:))), 'elif')) then
515 call handle_elif(ctx, macros, 'elif')
516 else if (starts_with(lowercase(adjustl(trimmed_line(2:))), 'else')) then
517 call handle_else(ctx)
518 else if (starts_with(lowercase(adjustl(trimmed_line(2:))), 'endif')) then
519 call handle_endif(ctx)
520 else if (starts_with(lowercase(adjustl(trimmed_line(2:))), 'pragma') .and. active) then
521 rst = ctx%content
522 else
523 return
524 end if
525 else if (active) then
526 if (.not. global%expand_macros .or. is_in_forloop()) then
527 rst = trimmed_line
528 else
529 rst = adjustl(expand_all(ctx, macros, stch, global%extra_macros, global%implicit_continuation, &
530 global%implicit_continuation))
531 end if
532 end if
533 end function
534end module
subroutine, public handle_ifndef(ctx, macros, token)
Process ifndef - test if a macro is NOT defined.
logical function, public is_active()
Determine whether the current source position is active.
subroutine, public handle_ifdef(ctx, macros, token)
Process ifdef - test if a macro is defined.
integer, public cond_depth
Current nesting depth of conditional directives (0 = outside any if).
subroutine, public handle_elif(ctx, macros, token)
Process elif - alternative branch after if/elif Only activates if no previous branch in the group was...
subroutine, public handle_elifndef(ctx, macros, token)
Process elifndef - test if a macro is not defined.
subroutine, public handle_else(ctx)
Process else - final fallback branch Activates only if no previous if/elif branch was true.
type(cond_state), dimension(max_cond_depth), public cond_stack
Global stack of conditional states (depth-limited).
subroutine, public handle_if(ctx, macros, token)
Process a if directive with constant expression evaluation Evaluates the expression after if using ev...
subroutine, public handle_elifdef(ctx, macros, token)
Process elifdef - test if a macro is defined.
subroutine, public handle_endif(ctx)
Process endif - end of conditional block Pops the top state from the stack. Reports error on unmatche...
subroutine, public handle_undef(ctx, macros, token)
Process a #undef directive.
Definition define.f90:298
subroutine, public handle_define(ctx, macros, token)
Process a #define directive.
Definition define.f90:151
subroutine, public handle_error(ctx, macros, token)
Process a #error directive.
subroutine, public handle_warning(ctx, macros, token)
Process a #warning directive.
subroutine, public handle_for(ctx, macros, token)
Process a #for directive and initialize a new loop context.
Definition loop.f90:162
subroutine, public add_to_loop(line)
Append a source line to the innermost active loop body.
Definition loop.f90:384
subroutine, public handle_endfor(ctx, ounit, p, macros, token)
Finalize a loop and emit all expanded iterations.
Definition loop.f90:290
logical function, public is_in_forloop()
Query whether parsing is currently inside a #for block. This routine is typically used by the main pr...
Definition loop.f90:398
type(global_settings), public global
Global preprocessor configuration instance.
Definition global.f90:192
recursive subroutine, public handle_include(ctx, ounit, preprocess, macros, token)
Process a include directive encountered during preprocessing Resolves the include file name (quoted o...
Definition include.f90:105
subroutine, public handle_line(ctx, token)
Process a #line directive.
Definition line.f90:105
character(:) function, allocatable, public expand_all(ctx, macros, stitch, has_extra, implicit_conti, dollar_insert)
Expand a source line including predefined macros.
Definition macro.f90:326
character function, public tail(str)
Returns the last non-blank character of a string.
Definition string.f90:746
pure character(len_trim(str)) function, public lowercase(str)
Convert string to lower case (respects contents of quotes).
Definition string.f90:852
character(:) function, allocatable, public concat(str1, str2)
Smart concatenation that removes continuation markers (&) and handles line-continuation rules.
Definition string.f90:763
logical function, public starts_with(str, arg1, idx)
Checks if a string starts with a given prefix Returns .true. if the string str (after trimming leadin...
Definition string.f90:715
character function, public head(str)
Returns the first character of the trimmed string.
Definition string.f90:732
Generic renderer for diagnostics and source excerpts.
Definition logging.f90:208
Return the number of stored macro definitions.
Definition macro.f90:223
Generic interface to start preprocessing from various sources/sinks.
Definition parser.f90:137
Locate the position of a substring.
Definition string.f90:395
Return the trimmed length of a string object.
Definition string.f90:201
Return the length of a string object.
Definition string.f90:164
Remove trailing blanks from a string object.
Definition string.f90:240
Snapshot of a source location within the preprocessing stream.
Definition context.f90:114
Structured compiler diagnostic.
Definition logging.f90:337
Diagnostic label identifying a region of source text.
Definition logging.f90:304
Representation of a preprocessor macro.
Definition macro.f90:135