These are my daily notes and internal conversations during development. They may be helpful to understand the larger context of decisions that were made, and how I learned and explored while building.

Devlog < 2023 < April < 4

| by date | | thread: co |

Spent a few hours last night digging around, reacquainting myself by writing and deleting some code. It seemed like I had a bit of an ergonomic issue with how things were architected, and the devlogs from my last focused work session give that impression as well. Now that I’m immersed into the codebase again, I feel like I can make some more architectural design decisions.

The “programming language” as it exists in software is just the tools that let you create and work with machine code. For Co, I want to be able to convert text into a binary source representation and back. I also want to be able to assemble binary source into an executable ROM, and extract/import symbols from the binary source. The first part of this is done: I can input some unicode text, and get out the binary representation. Converting binary to text should be simple, and I should worry about that later. Actually, it’s not done yet; I keep forgetting that I need to resolve all of the names. A name can either be local to the file/module, or resolved to a hash via the library. If a name is local, we need to make sure it still resolves every time we change the file. If a name is imported from the library, it should try to resolve it if it has no hash, but if it has a hash assigned we leave it unless we explicitly try to update.


I wonder if I could come at this from another angle. Instead of trying to enable the imports to be endlessly augmentable, perhaps it should just create a single “resolve log”. When you try to assemble it tries to use the resolve log, and fails if one doesn’t exist letting you know that you must generate a log. And so validating the source would generate a resolve file, which can be done separately from assembly. Actually, validating can just check if names resolve, and a separate resolve step can actually do the resolving. So to create a binary you first write source, then parse into byteco, then validate and optionally resolve, then assemble. To import the symbols, you first parse into byteco, then validate that hashes exist? What happens if the source knows what hashes it wants but you don’t have those hashes? Technically, you can still import the symbol, you just can’t load it, and you should probably be able to do that. So this is a different type of validation, making sure that every name resolves to a hash, not necessarily to byteco. Perhaps that’s an important distinction. Since macros are source code, those will need to be present though, it’s just routines that can be absent.

Okay, so `validate byteco` makes sure all names resolve to byteco, `validate hashes` makes sure all names resolve to hashes, `resolve` generates a resolve file linking imports to hashes using the local library, assemble creates a ROM by rendering macros/anchors/padding and appending routines, and import creates byteco symbols by converting macros to byteco and rendering routines to refer to other routines by hash.

When implementing this, it would be nice to not waste the validation pass. That validation pass could return a context that has already collected all of the byteco into neat packages for the assembler/importer to use.

| thread: co | | by date |