The first time I saw your table of data, Mark, I was fairly stupefied by what I was looking at. A damning indication that I had plenty of work ahead of me, despite my preliminary successes. (I had managed to compile a working version of Pong.asm/.hack, but I was using different memory registers... that's when I came here to poke around for ideas...)
At that time I had been using a C# Dictrionary<string, int> for my symbol-table. I saw how you've broken yours into C and D types... I didn't see any immediate benefit to that data-point, but it did nudge me into getting off the dictionary and simply making my own class/container for symbols. I still used the objects inside a C# List<SymbolEntry> to use List features, however.
Wow, that was way more time spent refactoring than most sane people would have applied, methinks, but... I finally got Pong.asm to compile and match the supplied Assembler!
Obviously(?) I could have just stuck with the dictionary. I think I'd have about half-as-much source-code right now, too, had I done so. Nevertheless, the exercise was entertaining and rewarding. Once my headache goes away, I'll have to go back and finish tidying/refactoring, but for now, I might have an award-winner for Most Bloated Hack Assembler:
Thanks, Mark, for the further explanation. As I say I didn't directly see any reason to include a data-type field when I went and refactored my symbol system. Yet, the fact that you'd generated it for debug had me thinking that if I did make a field for it, then as I wrote my code it might help me see/understand something that would get me "on track".
I think the truth is, as with my CPU and other challenges in this course, that my success came when I took the time to break the problem down into bite sized chunks of pseudo-code, then bring my C# (or HDL) up to the the task. My Main() is now rather concise, consisting of little more than this:
// Pre-parse input-stream of instructions into a handy-dandy List... let's call it: instructionList.
// (Get rid of whitespace, including blank lines and comments, that's for humans, not machines.)
PreParse(args, argument, instructionList);
// On first-pass, build requisite symbol table.
// [look for label declarations, track offset]
BuildSymbolTable(instructionList, debugLog, symbolTable);
/ Second-pass, link symbol table with variables.
nextOpenRegister = LinkVariables(instructionList, debugLog, nextOpenRegister, symbolTable);
// "Third-pass", Parse variables into absolute addresses.
// Parse and encode instructionList for machines* which are Hack-compliant.
List<string> encodedInstructions = DoEncode(instructionList, debugLog, ref nextOpenRegister, symbolTable);
// Output encoded data for machines*.
string programName = OutputProgram(args, argument, encodedInstructions);
// Output debug stream for humans.
...Suddenly it looks so easy.... why did it take me a week??! :)