define-directive-fixing.md
March 11, 2026 · View on GitHub
Directive normalization status
Goal
Allow legacy/invalid directive:
#define MY_MACRO "Hello"
…but treat it as a macro internally and provide an autofix to produce:
#macro MY_MACRO "Hello"
Workspace: Parser (@gmloop/parser)
Responsibility: During AST production, the parser should accept both spellings and emit a single normalized node so downstream tooling never special-cases #define.
Must:
- Parse
#macroand#defineinto the same node type (e.g.MacroDirective) - Normalize meaning to “macro”
- Preserve original spelling for diagnostics/fixes
- Record a precise range for the directive token for surgical edits
type MacroDirectiveNode = {
type: "MacroDirective";
// normalized meaning
directive: "macro";
// original keyword from source
keyword: "macro" | "define";
name: IdentifierNode;
value: ExpressionNode;
// full node range in original text
range: { start: number; end: number };
// range covering only "#macro" / "#define"
keywordRange: { start: number; end: number };
};
Notes:
- Do NOT create
DefineDirectiveas a separate node type. keywordRangeshould include the leading#and the word.
Workspace: Prettier Formatter (@gmloop/format)
Responsibility: Print MacroDirective reliably, regardless of whether the source used #define or #macro.
Must:
- Treat
MacroDirectiveas a macro directive node (no legacy handling) - Print using one of these policies:
Policy A (non-semantic formatting): preserve original spelling
- Output
#define ...whennode.keyword === "define"
Policy B (normalizing formatter, recommended): always print #macro
- Output
#macro ...for both spellings
Important: The formatter should never need to know “GML doesn’t support #define”; it just prints the node.
Workspace: Linter (@gmloop/lint)
Responsibility: Apply tolerant single-file source rewrites for legacy directive spellings and legacy block keywords before formatter-owned layout work.
Rule: normalize-directives
Current behavior:
- rewrites valid
#define NAME ...directives to#macro NAME ... - rewrites
#define region .../#define end region ...to#region/#endregion - uncomments legacy
//#region///#endregionlines - rewrites legacy
begin/endblock keywords to{/} - preserves invalid
#definespellings verbatim instead of guessing or commenting them out
Important:
- The rule is intentionally tolerant and line-oriented so it can run during Phase A safe-fix processing.
- Canonical
#macrodeclarations are left unchanged; formatter-owned spacing is not normalized here.
Checklist
Parser
- Parse both
#macroand#defineintoMacroDirective - Set
directive: "macro"for both - Preserve
keyword: "macro" | "define" - Record
keywordRangeprecisely
Formatter
- Print
MacroDirectiveas a macro directive - Choose policy A (preserve) or B (normalize to
#macro)
Linter
- Implement
normalize-directives - Rewrite valid legacy directive spellings and legacy block keywords during lint safe-fix passes
Tests
-
#define ...parses intoMacroDirective(keyword="define") - Printer output matches chosen policy
- Lint covers valid
#definemacros, region directives, and legacybegin/endrewrites