refactor: decompose macro parser into dedicated parsing stages #3

Merged
dalmurii merged 3 commits from watta0/Preproc:main into main 2026-06-24 15:34:48 +00:00
Contributor

Summary

Refactor the macro parser by decomposing the monolithic parsing logic into dedicated parsing stages and introducing a clearer state management model.

This change significantly improves readability, maintainability, and future extensibility while preserving the original behavior of the parser.

Motivation

The previous implementation contained most parsing logic inside main() and relied heavily on labels and cross-scope jumps. While functional, the control flow was difficult to follow and made future modifications risky.

This refactor aims to:

  • Reduce complexity in main()
  • Isolate parsing responsibilities
  • Improve naming consistency
  • Centralize error handling
  • Make individual parsing stages easier to understand and test

Changes

Parser decomposition

Split the parser into dedicated functions:

  • s_parse_book()

    • Detects and validates the ae2f_MAC( sequence.
  • s_parse_tparam()

    • Parses template parameter declarations.
  • s_parse_fnname()

    • Parses and emits function/named block.
  • s_parse_params()

    • Parses macro parameters and generates parameter comments.
  • s_parse_body()

    • Parses macro bodies and handles newline continuation conversion.
  • s_parse_include()

    • Handles #include directives.
  • s_parse_string()

    • Handles string literals and escape sequences.
  • s_parse_else()

    • Handles ordinary character output.

State management cleanup

Replaced anonymous state values with explicit enums:

enum RET_STATE_
enum FLAG_

This makes parser states and error conditions self-documenting and removes magic numbers from the implementation.

Naming improvements

Renamed several macros and constants to follow a more descriptive naming convention.

Examples:

  • SZPARAMPARAM_LEN
  • SZTPARAMTPARAM_LEN
  • isAlph()IS_ALPHA()
  • isNumber()IS_NUMBER()
  • isVarName()IS_VARNAME()
  • isNewLine()IS_NEWLINE()

Flag operations were also renamed:

  • isTrue()COMPARE_FLAG()
  • setTrue()SET_FLAG_TRUE()
  • setFalse()SET_FLAG_FALSE()

Unified error handling

Introduced helper macros:

__RET_IF_FAILED()
__RET_IF_EOF()

Benefits:

  • Eliminates repetitive error-checking code.
  • Makes failure paths consistent.
  • Improves readability of parser logic.

Buffer and constant cleanup

Introduced clearer length-related constants:

BOOK_LEN
PARAM_LEN
TPARAM_LEN

and removed several ad-hoc size calculations from parsing logic.

Improved separation of concerns

main() now acts primarily as a dispatcher:

  • Detect token category
  • Invoke the appropriate parser
  • Propagate parser results

This significantly reduces the amount of logic embedded in the main execution loop.

Benefits

  • Easier to understand parser flow
  • Smaller and more focused functions
  • Reduced cognitive complexity
  • Improved maintainability
  • Easier future feature additions
  • More consistent error handling
  • Better testability of individual parser stages

Notes

This refactor is intended to be behavior-preserving. No functional changes to the generated macro output are expected.

## Summary Refactor the macro parser by decomposing the monolithic parsing logic into dedicated parsing stages and introducing a clearer state management model. This change significantly improves readability, maintainability, and future extensibility while preserving the original behavior of the parser. ## Motivation The previous implementation contained most parsing logic inside `main()` and relied heavily on labels and cross-scope jumps. While functional, the control flow was difficult to follow and made future modifications risky. This refactor aims to: * Reduce complexity in `main()` * Isolate parsing responsibilities * Improve naming consistency * Centralize error handling * Make individual parsing stages easier to understand and test ## Changes ### Parser decomposition Split the parser into dedicated functions: * `s_parse_book()` * Detects and validates the `ae2f_MAC(` sequence. * `s_parse_tparam()` * Parses template parameter declarations. * `s_parse_fnname()` * Parses and emits function/named block. * `s_parse_params()` * Parses macro parameters and generates parameter comments. * `s_parse_body()` * Parses macro bodies and handles newline continuation conversion. * `s_parse_include()` * Handles `#include` directives. * `s_parse_string()` * Handles string literals and escape sequences. * `s_parse_else()` * Handles ordinary character output. ### State management cleanup Replaced anonymous state values with explicit enums: ```c enum RET_STATE_ enum FLAG_ ``` This makes parser states and error conditions self-documenting and removes magic numbers from the implementation. ### Naming improvements Renamed several macros and constants to follow a more descriptive naming convention. Examples: * `SZPARAM` → `PARAM_LEN` * `SZTPARAM` → `TPARAM_LEN` * `isAlph()` → `IS_ALPHA()` * `isNumber()` → `IS_NUMBER()` * `isVarName()` → `IS_VARNAME()` * `isNewLine()` → `IS_NEWLINE()` Flag operations were also renamed: * `isTrue()` → `COMPARE_FLAG()` * `setTrue()` → `SET_FLAG_TRUE()` * `setFalse()` → `SET_FLAG_FALSE()` ### Unified error handling Introduced helper macros: ```c __RET_IF_FAILED() __RET_IF_EOF() ``` Benefits: * Eliminates repetitive error-checking code. * Makes failure paths consistent. * Improves readability of parser logic. ### Buffer and constant cleanup Introduced clearer length-related constants: ```c BOOK_LEN PARAM_LEN TPARAM_LEN ``` and removed several ad-hoc size calculations from parsing logic. ### Improved separation of concerns `main()` now acts primarily as a dispatcher: * Detect token category * Invoke the appropriate parser * Propagate parser results This significantly reduces the amount of logic embedded in the main execution loop. ## Benefits * Easier to understand parser flow * Smaller and more focused functions * Reduced cognitive complexity * Improved maintainability * Easier future feature additions * More consistent error handling * Better testability of individual parser stages ## Notes This refactor is intended to be behavior-preserving. No functional changes to the generated macro output are expected.
@ -332,4 +334,0 @@
return 1;
casenewlines:
ENDL[2] = (char) ch;
Owner

ENDL is used only here and modified. Declare this variable as under s_parse_body.

ENDL is used only here and modified. Declare this variable as under `s_parse_body`.
Author
Contributor

got it!

got it!
Macro/main.c Outdated
@ -1,22 +1,21 @@
#define _CRT_SECURE_NO_WARNINGS 1
#include <.main.h>
#include <assert.h>
Owner

That header is used nowhere.

That header is used nowhere.
Author
Contributor

Order received!

Order received!
Macro/main.c Outdated
@ -18,2 +16,2 @@
#define BOOK (ae2f_MAC_KEYWORD "(")
#define STRLEN(a) ((int)(sizeof(a) - 1))
#define BOOK (ae2f_MAC_KEYWORD "(")
#define BOOK_LEN ((size_t)(sizeof(BOOK) - (size_t)1))
Owner

Duplicated. Would you utilise STRLEN to make BOOK_LEN?

Duplicated. Would you utilise STRLEN to make BOOK_LEN?
Author
Contributor

Got it!

Got it!
@ -66,0 +46,4 @@
#define __RET_IF_FAILED(ret_var, expr) \
do { \
(ret_var) = (expr); \
if ((ret_var) != RET_STATE_OK) \
Owner
same as https://talkischeap.work.gd/ae2f/Preproc/pulls/3/files#issuecomment-154
Author
Contributor

Got it

Got it
@ -66,0 +53,4 @@
#define __RET_IF_EOF(expr) \
do { \
if ((expr) == EOF) \
return RET_STATE_OUTFAILED; \
Owner

enum definition is below this macro. move that please.

enum definition is below this macro. move that please.
Author
Contributor

Got it

Got it
Macro/main.c Outdated
@ -66,0 +80,4 @@
static enum RET_STATE_ s_parse_fnname (flags_t *rdwr_flags);
static enum RET_STATE_ s_parse_params (flags_t *rdwr_flags);
static enum RET_STATE_ s_parse_body (flags_t *rdwr_flags);
static enum RET_STATE_ s_parse_else (int ch);
Owner

This name does not explain what is this else of.
I suggest s_parse_fallback of s_parse_default

This name does not explain what is this else of. I suggest `s_parse_fallback` of `s_parse_default`
Author
Contributor

Got it

Got it
@ -94,1 +99,3 @@
return STATE_OUTFAILED;
switch (ch) {
case '\"':
__RET_IF_FAILED(ret, s_parse_string(ch));
Owner

You know what ch is. Using literal would be helpful.

You know what `ch` is. Using literal would be helpful.
Author
Contributor

Order received

Order received
@ -95,0 +101,4 @@
__RET_IF_FAILED(ret, s_parse_string(ch));
break;
case '#':
__RET_IF_FAILED(ret, s_parse_include(ch));
Owner
Same as https://talkischeap.work.gd/ae2f/Preproc/pulls/3/files#issuecomment-156
Author
Contributor

Got it

Got it
Macro/main.c Outdated
@ -289,2 +308,2 @@
PARAMS_BUF[prms_len] = (char) ch;
PARAMS_BUF[prms_len + 1] = 0;
buf[buf_len] = (char)ch;
buf[buf_len + 1] = '\0';
Owner

That needs former length test

That needs former length test
Author
Contributor

got it

got it
@ -303,2 +315,2 @@
PARAMS_BUF[prms_len + 4] = 0;
}
if (ch == ')') {
buf[buf_len] = ' ';
Owner

That is unnecessary as I see now.
This was my mistake and you seem to be shipping it as is.

Putting putsas is and printing this statement later would be more stable.

That is unnecessary as I see now. This was my mistake and you seem to be shipping it as is. Putting `puts`as is and printing this statement later would be more stable.
Author
Contributor

got it

got it
Owner

It is not working.

It is not working.
Sign in to join this conversation.
No description provided.