Here's my design proposal, I've decided to limit this proposal to the SILInstruction parser. Assuming this goes well, I plan to extend this design to other parsers, such as the type parser, and top-level declaration parser, but, for now, I think it's best to limit myself to the SILInstruction parser.
Yesterday I posted a rough outline of the API that I want to implement but, I don't think I did a very good job conveying exactly how those functions that I defined work together. If you're like me in the sense that you can understand something best by looking through some code, you can take a look at this PR I made. Otherwise, keep reading :)
I like to think about what's happening as an assembly line. We start with the raw text, and we want the final result to be a polished SILInstruction that can be a node of information used in the compiler.
Like an assembly line, we need to do different operations to the node as it makes its way from the raw text form to the polished SILInstruction form. Also like an assembly line, these operations should happen synchronously—one after another. A linear code sequence, as Chris put it. I've broken these operations down into three steps: "read," "check," and "emit."
"Read" is the first operation. The "ReadSIL" class is a "Parser" (and is used as the parser during the transition period). The operation this step performs is text to information. We have to extract the information from the text we receive. This step doesn't care if the information makes sense or fits together; it just reads it and gives it to the next step.
Note: If the text is so malformed that the information cannot be read, this step simply exits early, allowing the next step to diagnose the issue. If the text is so malformed that the reader cannot even determine what SILInstruction is being parsed, then an error diagnostic is emitted.
"Check" is the next operation. The "CheckSIL" class is a basic checker whose only job is to make sure that the information we have makes sense. It ensures that the information we read, in the form of a SILParserResult , can be used to create a SILInstruction.
"Emit" is the last operation. The "EmitSIL" class does the final step. It turns the valid information we have into a SILInstruction. A SILInstruction must be able to be created given the information provided to this visitor.
As you can see, these steps all fit together sequentially and communicate by passing around information in the form of a SILParserResult . This allows for maximum separation of concerns both at an "operation" level and at an instruction level.