Skip to content

hrw4u: add --error-format flag with pluggable formatters#13147

Open
samtes wants to merge 5 commits intoapache:masterfrom
samtes:hrw4u-error-format
Open

hrw4u: add --error-format flag with pluggable formatters#13147
samtes wants to merge 5 commits intoapache:masterfrom
samtes:hrw4u-error-format

Conversation

@samtes
Copy link
Copy Markdown

@samtes samtes commented May 8, 2026

Motivation

hrw4u / u4wrh only emit human-readable error output, which makes it hard for editors, CI jobs, and PR bots to consume diagnostics. This PR introduces structured output formats (JSON and Markdown) while keeping the existing plain-text output as the default, and tightens a few rough edges around error reporting and the build.

Summary

  • Adds --error-format {plain,json,markdown} to hrw4u / u4wrh, backed by a new pluggable ErrorFormatter hierarchy in src/formatters.py.
  • Routes non-syntax failures (file I/O, argument errors, visitor errors) through the chosen formatter so JSON/Markdown consumers see every diagnostic.
  • Drops the Found 1 error: preamble for single-error plain output.
  • Fails fast in the Makefile with a helpful hint when the ANTLR generator is missing from PATH.

Test plan

  • cd tools/hrw4u && make test passes.
  • hrw4u --error-format plain <bad.hrw4u> output is byte-identical to master (minus the Found 1 error: line for single errors).
  • hrw4u --error-format json <bad.hrw4u> parses as JSON and includes schema_version: 1.
  • hrw4u --error-format markdown <bad.hrw4u> renders cleanly when pasted into a GitHub PR comment.
  • With antlr hidden from PATH, make gen exits with the install hint instead of a cryptic failure.

samtes added 5 commits May 6, 2026 13:35
Adds a check-antlr Make target that errors with an install hint
(brew/dnf/apt/bootstrap.sh) before attempting parser generation,
instead of surfacing a cryptic rule failure deep in gen-fwd on a
fresh checkout.
Introduces an ErrorFormatter ABC with PlainText, JSON, and Markdown
implementations. ErrorCollector now delegates rendering instead of
building the output string inline, with PlainText as the default so
existing callers see byte-for-byte identical output. The JSON schema
is versioned (v1) and emitted on a single line so bulk-mode runs
produce NDJSON on stderr.
Wires the formatter abstraction into the CLI with a new
--error-format {plain,json,markdown} option (default: plain, so
existing consumers are unaffected). Non-syntax errors (file I/O,
argument errors) now flow through the same formatter path via
new emit_fatal_message / emit_fatal_error helpers, so downstream
tools see one stable schema regardless of where the error came from.
When only one error is produced, the 'Found 1 error:' summary line
adds noise without adding information. Skip it in that case and keep
the 'Found N errors:' header for counts of two or more.
A trailing comma on the last parameter forced yapf to keep one
argument per line, which clashes with the project's 132-column
horizontal style. Drop the trailing commas in the new formatter
signatures, call sites, and dict literals so yapf collapses them.
@zwoop zwoop requested review from bneradt, Copilot and zwoop May 9, 2026 06:09
@zwoop zwoop added the hrw4u label May 9, 2026
@zwoop zwoop added this to the 11.0.0 milestone May 9, 2026
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new --error-format {plain,json,markdown} flag to hrw4u / u4wrh so diagnostics can be consumed by editors/CI/bots, implemented via a pluggable formatter layer while keeping plain text as the default.

Changes:

  • Introduces ErrorFormatter implementations (plain/JSON/Markdown) and a formatter registry.
  • Routes parser/visitor/file I/O failures through the selected formatter and updates tests to lock in output contracts.
  • Improves tools/hrw4u build behavior with an early ANTLR-on-PATH check.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
tools/hrw4u/src/formatters.py New formatter hierarchy + registry for plain/JSON/Markdown diagnostic output.
tools/hrw4u/src/errors.py Extends ErrorCollector to delegate rendering to a formatter; stores raw error message on Hrw4uSyntaxError.
tools/hrw4u/src/common.py Adds --error-format CLI flag and routes fatal/non-syntax errors through formatter-aware helpers.
tools/hrw4u/Makefile Adds check-antlr target and wires it into generation.
tools/hrw4u/tests/test_errors.py Adds unit tests for formatter behavior and schema expectations.
tools/hrw4u/tests/test_cli.py Adds CLI-level tests for --error-format behavior across parse/I/O failures and --help.
tools/hrw4u/tests/test_units.py Updates unit expectations for the removed single-error “Found 1 error:” preamble.
Comments suppressed due to low confidence (1)

tools/hrw4u/Makefile:160

  • check-antlr is wired into gen-fwd, but gen-inv still invokes $(ANTLR) without the fast-fail prerequisite. This means make gen-inv (or any target that depends only on inverse generation) will still fail with the original cryptic error when ANTLR is missing. Add check-antlr as a dependency of gen-inv as well.
# Generate forward parser/lexer into build/hrw4u and build/hrw4u-lsp
gen-fwd: check-antlr $(ANTLR_FILES_FWD)

$(ANTLR_FILES_FWD): $(GRAMMAR_FWD)
	@mkdir -p $(PKG_DIR_HRW4U)
	cd grammar && $(ANTLR) -Dlanguage=Python3 -visitor -no-listener -o ../$(PKG_DIR_HRW4U) hrw4u.g4

# LSP no longer generates its own ANTLR files - it imports from hrw4u

# Generate inverse parser/lexer into build/u4wrh
gen-inv: $(ANTLR_FILES_INV)

Comment thread tools/hrw4u/src/common.py
Comment on lines +116 to +118
def _build_formatter(error_format: str):
"""Instantiate the configured error formatter, falling back to plain."""
return FORMATTERS.get(error_format, FORMATTERS["plain"])()
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, we encourage the -> return types as much as possible

Comment thread tools/hrw4u/src/common.py
return None, parser_obj, error_collector
else:
fatal(f"{filename}:0:0 - {error_prefix} error: {e}")
emit_fatal_message(error_format, f"{filename}:0:0 - {error_prefix} error: {e}", filename=filename)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants