Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 15 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -301,8 +301,8 @@ compare-test-export-params-to-udb: $(EXPECTED_EXPORT_PARAMS_TO_UDB_DIR) $(BUILT_
#
# Test tag change detection
#
.PHONY: test-tag-changes test-tag-changes-basic test-tag-changes-verbose test-tag-changes-no-changes test-tag-changes-additions-only test-tag-changes-whitespace-only test-tag-changes-formatting-only test-tag-changes-update test-tag-changes-strict-additions test-tag-changes-strict-formatting test-tag-changes-strict-whitespace test-tag-changes-strict-update
test-tag-changes: test-tag-changes-basic test-tag-changes-verbose test-tag-changes-no-changes test-tag-changes-additions-only test-tag-changes-whitespace-only test-tag-changes-formatting-only test-tag-changes-update test-tag-changes-strict-additions test-tag-changes-strict-formatting test-tag-changes-strict-whitespace test-tag-changes-strict-update
.PHONY: test-tag-changes test-tag-changes-basic test-tag-changes-verbose test-tag-changes-no-changes test-tag-changes-additions-only test-tag-changes-whitespace-only test-tag-changes-formatting-only test-tag-changes-macros test-tag-changes-update test-tag-changes-strict-additions test-tag-changes-strict-formatting test-tag-changes-strict-whitespace test-tag-changes-strict-macros test-tag-changes-strict-update
test-tag-changes: test-tag-changes-basic test-tag-changes-verbose test-tag-changes-no-changes test-tag-changes-additions-only test-tag-changes-whitespace-only test-tag-changes-formatting-only test-tag-changes-macros test-tag-changes-update test-tag-changes-strict-additions test-tag-changes-strict-formatting test-tag-changes-strict-whitespace test-tag-changes-strict-macros test-tag-changes-strict-update

test-tag-changes-basic: $(TAG_CHANGES_TEST_REFERENCE_PATH) $(TAG_CHANGES_TEST_CURRENT_PATH)
@echo "TESTING TAG CHANGE DETECTION - BASIC OUTPUT (with modifications/deletions)"
Expand All @@ -328,6 +328,13 @@ test-tag-changes-formatting-only: $(TAG_CHANGES_TEST_REFERENCE_PATH)
@echo "TESTING TAG CHANGE DETECTION - FORMATTING ONLY (expect exit 0)"
$(DETECT_TAG_CHANGES_PYTHON) $(TAG_CHANGES_TEST_REFERENCE_PATH) $(TAG_CHANGES_TESTS_DIR)/formatting-only.json && echo "test-tag-changes-formatting-only PASSED" || echo "test-tag-changes-formatting-only FAILED (formatting-only changes should return exit 0)"

# RISC-V macro migration (csr:/ext:/insn:), binary renotation (11b -> 0b11) and
# en dash (-- -> {endash}) are markup-only: they must normalize to equal text
# and report no change.
test-tag-changes-macros: $(TAG_CHANGES_TESTS_DIR)/macros-plain.json $(TAG_CHANGES_TESTS_DIR)/macros-markup.json
@echo "TESTING TAG CHANGE DETECTION - RISC-V MACROS (expect exit 0)"
$(DETECT_TAG_CHANGES_PYTHON) $(TAG_CHANGES_TESTS_DIR)/macros-plain.json $(TAG_CHANGES_TESTS_DIR)/macros-markup.json && echo "test-tag-changes-macros PASSED" || echo "test-tag-changes-macros FAILED (macro-only changes should return exit 0)"

test-tag-changes-update: $(TAG_CHANGES_TEST_REFERENCE_PATH)
@echo "TESTING TAG CHANGE DETECTION - UPDATE FILE"
@cp -f $(TAG_CHANGES_TEST_REFERENCE_PATH) $(BUILD_DIR)/test-reference.json
Expand All @@ -347,6 +354,12 @@ test-tag-changes-strict-formatting: $(TAG_CHANGES_TEST_REFERENCE_PATH)
@echo "TESTING TAG CHANGE DETECTION - STRICT, FORMATTING ONLY (expect exit 1)"
$(DETECT_TAG_CHANGES_PYTHON) --strict $(TAG_CHANGES_TEST_REFERENCE_PATH) $(TAG_CHANGES_TESTS_DIR)/formatting-only.json && echo "test-tag-changes-strict-formatting FAILED (strict should fail on formatting-only changes)" || echo "test-tag-changes-strict-formatting PASSED"

# Strict mode compares prose byte-for-byte, so the macro forms must NOT be
# normalized away: this is a freshness gate, not a review signal.
test-tag-changes-strict-macros: $(TAG_CHANGES_TESTS_DIR)/macros-plain.json $(TAG_CHANGES_TESTS_DIR)/macros-markup.json
@echo "TESTING TAG CHANGE DETECTION - STRICT, RISC-V MACROS (expect exit 1)"
$(DETECT_TAG_CHANGES_PYTHON) --strict $(TAG_CHANGES_TESTS_DIR)/macros-plain.json $(TAG_CHANGES_TESTS_DIR)/macros-markup.json && echo "test-tag-changes-strict-macros FAILED (strict should fail on macro-only changes)" || echo "test-tag-changes-strict-macros PASSED"

test-tag-changes-strict-whitespace: $(TAG_CHANGES_TEST_REFERENCE_PATH)
@echo "TESTING TAG CHANGE DETECTION - STRICT, WHITESPACE ONLY (expect exit 0)"
$(DETECT_TAG_CHANGES_PYTHON) --strict $(TAG_CHANGES_TEST_REFERENCE_PATH) $(TAG_CHANGES_TESTS_DIR)/whitespace-only.json && echo "test-tag-changes-strict-whitespace PASSED" || echo "test-tag-changes-strict-whitespace FAILED (strict should still tolerate whitespace-only changes)"
Expand Down
7 changes: 7 additions & 0 deletions tests/tag-changes/macros-markup.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"tags": {
"norm:csr-plain": "Set csr:mip[] and read csr:vsstatus[sdt] while csr::[fs] and csr:[tm] are checked.",
"norm:ext-insn-plain": "The ext:f[] extension defines insn:wrs.sto[].",
"norm:binary-endash-plain": "Encode 11b across the 0b00--0b11 span."
}
}
7 changes: 7 additions & 0 deletions tests/tag-changes/macros-plain.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"tags": {
"norm:csr-plain": "Set mip and read vsstatus.SDT while FS and TM are checked.",
"norm:ext-insn-plain": "The F extension defines wrs.sto.",
"norm:binary-endash-plain": "Encode 0b11 across the 0b00{endash}0b11 span."
}
}
57 changes: 56 additions & 1 deletion tools/detect_tag_changes.py
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,59 @@ def _normalize_whitespace(self, text: str) -> str:
"""
return re.sub(r'\s+', ' ', text.strip())

def _normalize_riscv_macros(self, text: str) -> str:
"""Normalize RISC-V AsciiDoc macros to their plain-text equivalent.

The tags backend stores raw AsciiDoc (see #169), so a pure markup
migration such as ``mip`` -> ``csr:mip[]`` must not be reported as a
change to normative text. These are the semantic macros used across
the ISA manual:

csr:NAME[] -> NAME (csr:mip[] -> mip)
csr:NAME[FIELD] -> NAME.FIELD (csr:vsstatus[sdt] -> vsstatus.SDT)
csr:[FIELD] -> FIELD (csr:[tm] -> TM)
csr::[FIELD] -> FIELD (csr::[fs] -> FS)
ext:NAME[] -> NAME (ext:f[] -> F)
insn:NAME[] -> NAME (insn:wrs.sto[] -> wrs.sto)

CSR names keep their case; CSR field names and extension names render
upper-case, matching how they read as prose.

Note this normalization is for the reviewer-facing change *report*
only. It deliberately erases formatting differences, which is correct
for "did the normative text change" and wrong for "is this file byte
up to date" -- do not reuse it as a freshness check.

Args:
text: Text to normalize

Returns:
Text with RISC-V macros reduced to plain text
"""
def _csr_named(match: "re.Match") -> str:
name, field = match.group(1), match.group(2)
return f"{name}.{field.upper()}" if field else name

# csr:NAME[FIELD] and csr:NAME[] (name present, field optional).
result = re.sub(r'csr:([\w.]+)\[([\w.]*)\]', _csr_named, text)
# csr:[FIELD] and csr::[FIELD] (name absent).
result = re.sub(r'csr::?\[([\w.]+)\]',
lambda m: m.group(1).upper(), result)
# ext:NAME[] -> NAME (capitalized as prose does: single-letter base
# extensions read as "F"/"D"; named ones as "Sscofpmf"/"Zicsr").
result = re.sub(r'ext:([\w.]+)\[\]',
lambda m: m.group(1).capitalize(), result)
# insn:NAME[] -> NAME.
result = re.sub(r'insn:([\w.]+)\[\]', r'\1', result)

# Binary renotation: normalize both 11b and 0b11 spellings to 0b-prefixed.
result = re.sub(r'\b([01]+)b\b', r'0b\1', result)

# En dash: -- -> {endash} (leave em dash --- untouched).
result = re.sub(r'(?<!-)--(?!-)', '{endash}', result)

return result

def _strip_asciidoc_formatting(self, text: str) -> str:
"""Strip AsciiDoc formatting marks.

Expand All @@ -249,7 +302,9 @@ def _strip_asciidoc_formatting(self, text: str) -> str:
Returns:
Text without AsciiDoc formatting
"""
result = text
# Reduce RISC-V semantic macros first so a macro wrapped in other
# formatting is still recognized before the generic strips run.
result = self._normalize_riscv_macros(text)

# Remove bold: **text** (unconstrained) or *text* (constrained)
result = re.sub(r'\*\*([^\*]+?)\*\*', r'\1', result)
Expand Down
Loading