Say that an option's file is one the assembler writes, and check we said it
-S was added without a row in the Assembler Manual, and the usage it printed listed a bare "-S <file>" with no long name and no statement of what the file is for. That is not merely incomplete, it is misleading: "-S <file>" reads just as naturally as "dump the symbols of <file>", and asking for it that way hands the source to -S, leaves nothing positional behind it, and is answered with "No source file specified" on a command line that plainly names a source. The error described the hole the mistake left and hid the mistake. So the usage now prints the long names, says outright that every <file> is a path it writes and the source is the last argument on its own, and ends with a whole example command. When the source is missing and a file-taking option was given, the error says which options take a path to write. The manual gains the -S row it never had, a warning in the same words, and a sentence on what a symbol dump is for. Documenting it twice is how it went wrong once, so docs.sh now settles both against getopt's own option table: every option the assembler takes has a row in the manual and a line in its own usage. Verified with break.sh against the manual row and the usage line separately. 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
cdee9acfae
commit
7a55cfe151
@@ -95,15 +95,28 @@ void assemblerCleanup(intermediateElement *intermediateArray, int arraySize, cha
|
||||
free(outputFileName);
|
||||
}
|
||||
|
||||
// EVERY OPTION HERE NAMES A FILE THE ASSEMBLER WRITES, and the source is the bare argument
|
||||
// at the end. A list of bare flags does not say that: "-S <file>" reads just as naturally as
|
||||
// "dump the symbols of <file>", and asking for it that way hands the source to -S, leaves
|
||||
// nothing positional behind it, and gets answered with "No source file specified" - which
|
||||
// describes the symptom and hides the cause. So the long names are printed, every line says
|
||||
// where the file is written, and an example shows a whole command rather than leaving one to
|
||||
// be assembled out of the parts.
|
||||
void printUsage(const char *programName) {
|
||||
printf("Usage: %s [OPTIONS] <sourcefile>\n", programName);
|
||||
printf("\n");
|
||||
printf("Assembles one source file. The source is the last argument, on its own; every\n");
|
||||
printf("<file> below is a path the assembler writes, and every <dir> one it searches.\n");
|
||||
printf("\n");
|
||||
printf("Options:\n");
|
||||
printf(" -o <file> Write the output to this path instead of alongside the source.\n");
|
||||
printf(" -I <dir> Look in this directory for included files. May be given more than once.\n");
|
||||
printf(" -M <file> Write the source files this output depends on, as a make rule.\n");
|
||||
printf(" -S <file> Write every label and the address it was given, in address order.\n");
|
||||
printf(" -h, --help Display this help message.\n");
|
||||
printf(" -o, --output <file> Write the assembled output here, instead of alongside the source.\n");
|
||||
printf(" -I, --include <dir> Search this directory for included files. May be given more than once.\n");
|
||||
printf(" -M, --depend <file> Write a make rule here, naming every source that went into the output.\n");
|
||||
printf(" -S, --symbols <file> Write every label here with the address it was given, in address order.\n");
|
||||
printf(" -h, --help Display this help message.\n");
|
||||
printf("\n");
|
||||
printf("Example:\n");
|
||||
printf(" %s -I Libraries -S game.sym -o game.sbx game.asm\n", programName);
|
||||
}
|
||||
|
||||
// Writes a make rule naming every source file that went into the output, so that a
|
||||
@@ -207,6 +220,14 @@ int main(int argc, char *argv[]) {
|
||||
|
||||
if (optind >= argc) {
|
||||
fprintf(stderr, RED "Error: No source file specified.\n" RESET);
|
||||
// The commonest way to get here is giving the source to an option that wanted a
|
||||
// path to write, which consumes it and leaves nothing positional. Saying so is the
|
||||
// difference between an error that names the mistake and one that names the hole
|
||||
// the mistake left behind.
|
||||
if (outputFileName || dependencyFileName || symbolFileName) {
|
||||
fprintf(stderr, " -o, -M and -S each name a file to WRITE, not one to read.\n"
|
||||
" The source file is the last argument, on its own.\n");
|
||||
}
|
||||
printUsage(argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -420,8 +420,13 @@ Assembler [options] <sourcefile>
|
||||
| -o, --output \<file\> | Write the output to this path. Without it, the output is named after the source file, in the directory the assembler was run from, taking .bin if it is a boot image and .sbx if it is a loadable program. |
|
||||
| -I, --include \<dir\> | Look in this directory for included files. May be given more than once, and the directories are searched in the order given. |
|
||||
| -M, --depend \<file\> | Write out which source files went into the output, as a make rule. |
|
||||
| -S, --symbols \<file\> | Write every label and the address it was given, in address order, to this path. |
|
||||
| -h, --help | Print the options and stop. |
|
||||
|
||||
Every option above that takes a \<file\> names a file the assembler WRITES, and the source is the bare argument at the end. So `Assembler -S program.asm` does not dump the symbols of program.asm. It asks for the symbol file to be called program.asm, and then finds it has no source left to assemble.
|
||||
|
||||
-S is for looking at what the assembler decided. Every label in the program, in address order, with the address it ended up at: which segment a name landed in, how far apart two routines really are, and whether the label you are looking for was assembled at all.
|
||||
|
||||
The assembler stops at the first error, says which file and line it was in, and exits without writing anything.
|
||||
|
||||
## Building With Make:
|
||||
|
||||
@@ -172,6 +172,40 @@ else:
|
||||
problems.append("%s (0x%02X) is a device class and has no row in the Devices"
|
||||
" table" % (name, value))
|
||||
|
||||
# ---- Every option the assembler takes is written down in both places ----
|
||||
#
|
||||
# An option added to getopt is an option nobody knows about until it is said twice: in the
|
||||
# manual's table, and in the usage the assembler prints when it is asked for help or refuses
|
||||
# a command line. -S arrived with neither, and the damage was worse than an undocumented
|
||||
# switch. The usage listed a bare "-S <file>" with no long name and no statement that the
|
||||
# file is one it WRITES, so it read as "dump the symbols of <file>" - which hands the source
|
||||
# to -S, leaves nothing positional behind it, and gets answered with "No source file
|
||||
# specified" on a command line that plainly names one.
|
||||
#
|
||||
# The option table is the source of truth because it is what getopt_long is actually given.
|
||||
assembler = read("Source/Assembler/Assembler.c")
|
||||
options = re.findall(r'^\s*\{"([a-z]+)",\s*\w+,\s*0,\s*\'(\w)\'\s*\},', assembler, re.M)
|
||||
if not options:
|
||||
problems.append("could not find the assembler's option table")
|
||||
elif "void printUsage" not in assembler:
|
||||
problems.append("the assembler has lost its printUsage")
|
||||
elif "## Running the Assembler:" not in am:
|
||||
problems.append("the Assembler Manual has lost its options table")
|
||||
else:
|
||||
usage = assembler.split("void printUsage")[1].split("\n}")[0]
|
||||
section = am.split("## Running the Assembler:")[1].split("\n## ")[0]
|
||||
# Only the table rows count, so a switch merely mentioned in the prose below it does not
|
||||
# pass for a documented one.
|
||||
rows = re.findall(r'^\| (-\w, --[a-z]+)', section, re.M)
|
||||
for longName, shortName in options:
|
||||
spelled = "-%s, --%s" % (shortName, longName)
|
||||
if spelled not in rows:
|
||||
problems.append("the assembler takes %s and the Assembler Manual's option table"
|
||||
" has no row for it" % spelled)
|
||||
if spelled not in usage:
|
||||
problems.append("the assembler takes %s and its own usage message does not name"
|
||||
" it" % spelled)
|
||||
|
||||
# ---- The vector ranges the manuals quote are the ones the assembler uses ----
|
||||
#
|
||||
# Both manuals print the boundary between numbers a program may pin and numbers the
|
||||
|
||||
Reference in New Issue
Block a user