A manual for writing tunes
The fifth document, and the one somebody would want first if they had a piece of music and this machine. What a tune is, how to write one, what the compiler refuses and why, how to build and play one, and the bytes it holds for anything that would rather write one itself. The last of those matters more than it looks: Tests/maketune.py writes tunes without going through TuneC, and the suite checks the two agree byte for byte. A format with two implementations needs a specification they are both held to rather than one of them being the specification. The refusals get a table of their own with the reason beside each, because every one of them is something the PLAYER cannot notice - it has no names, no lengths, and no way to tell "no starting instrument" from "instrument nought" once a tune is loaded. Documented as reasons rather than as rules, so that somebody meeting one knows what it saved them from. docs.sh now settles the manual against the compiler both ways: every directive TuneC takes has a row, and the limits the manual quotes are the compiler's own #defines. Verified with break.sh - a directive removed from the manual and a limit raised in the compiler are both caught. One that was not caught first time and should not have been: removing the #Use row from one table left it documented in the other, which is the check being right and my break being wrong. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01E2JrLzFvuFX9fgi1LDRjrW
This commit is contained in:
co-authored by
Claude Opus 5
parent
2808688fa1
commit
fb672d7761
@@ -956,6 +956,46 @@ else:
|
||||
% (name, at, source, lines[at - 1].strip()))
|
||||
break
|
||||
|
||||
# ---- Every directive TuneC takes has a row in the Tune Manual ----
|
||||
#
|
||||
# The same check the assembler's options get, for the same reason: a directive added to the
|
||||
# compiler and not written down is one nobody can use, and one written down and removed is
|
||||
# worse - it is an instruction that fails.
|
||||
#
|
||||
# The manual also carries the limits, which are #defines and exactly the sort of number that
|
||||
# goes stale silently: raising one leaves a sentence looking perfectly reasonable and wrong.
|
||||
tunec = read("Source/Tune/TuneC.c")
|
||||
tm = read("SplitBit Tune Manual.md")
|
||||
|
||||
directives = sorted(set(re.findall(r'strcmp\(t->text, "(#\w+)"\)', tunec)) |
|
||||
set(re.findall(r'strcmp\(t->text, "(#Use)"\)', tunec)))
|
||||
if not directives:
|
||||
problems.append("could not find the directives TuneC understands")
|
||||
else:
|
||||
for directive in directives:
|
||||
if not re.search(r'^\| %s[ \\]' % re.escape(directive), tm, re.M):
|
||||
problems.append("TuneC takes %s and the Tune Manual has no row for it" % directive)
|
||||
|
||||
limits = {name: int(value)
|
||||
for name, value in re.findall(r'^#define (MAX_\w+|VOICES)\s+(\d+)', tunec, re.M)}
|
||||
said = {
|
||||
"VOICES": r'^\| Voices \| (\d+) \|',
|
||||
"MAX_PATCHES": r'^\| Patches \| (\d+) \|',
|
||||
"MAX_SEQUENCES": r'^\| Sequences \| (\d+)\.',
|
||||
"MAX_EVENTS": r'^\| Events in a sequence \| (\d+) ',
|
||||
"MAX_ORDER": r'^\| Entries in an order list \| (\d+) \|',
|
||||
}
|
||||
for name, pattern in said.items():
|
||||
if name not in limits:
|
||||
problems.append("TuneC no longer defines %s, which the Tune Manual quotes" % name)
|
||||
continue
|
||||
m = re.search(pattern, tm, re.M)
|
||||
if not m:
|
||||
problems.append("the Tune Manual has lost its %s limit" % name)
|
||||
elif int(m.group(1)) != limits[name]:
|
||||
problems.append("the Tune Manual says %s is %s and TuneC says %d"
|
||||
% (name, m.group(1), limits[name]))
|
||||
|
||||
if problems:
|
||||
print("The manuals and the code disagree:")
|
||||
for p in problems:
|
||||
|
||||
Reference in New Issue
Block a user