These rules are embedded into the semantic analyzer while creating the AST nodes for an expression. Likewise, the type and the constant value can be computed.
It should be noted that not all kinds are computation can be done in this way. For example, to detect the use of uninitialized variables, a method called symbolic interpretation can be used. In its general form, the method requires a special walk order through the AST, which is not possible during construction time. The good news is that the presented approach creates a fully decorated AST that is ready for code generation. This AST can be used for further analysis, given that costly analysis can be turned on or off on demand.
To play around with the frontend, you also need to update the driver. Since the code generation is missing, a correct tinylang program produces no output. Still, it can be used to explore error recovery and provoke semantic errors:

include “tinylang/Basic/Diagnostic.h”
include “tinylang/Basic/Version.h”
include “tinylang/Parser/Parser.h”
include “llvm/Support/InitLLVM.h”
include “llvm/Support/raw_ostream.h”
using namespace tinylang;
int main(int argc_, const char **argv_) {
llvm::InitLLVM X(argc_, argv_);
llvm::SmallVector argv(argv_ + 1,
argv_ + argc_);
llvm::outs() << “Tinylang ” << tinylang::getTinylangVersion() << “\n”; for (const char *F : argv) { llvm::ErrorOr>
FileOrErr = llvm::MemoryBuffer::getFile(F);
if (std::error_code BufferError =
FileOrErr.getError()) {
llvm::errs() << “Error reading ” << F << “: “
<< BufferError.message() << “\n”;
continue;
}
llvm::SourceMgr SrcMgr;
DiagnosticsEngine Diags(SrcMgr);
SrcMgr.AddNewSourceBuffer(std::move(*FileOrErr),
llvm::SMLoc());
auto TheLexer = Lexer(SrcMgr, Diags);
auto TheSema = Sema(Diags);
auto TheParser = Parser(TheLexer, TheSema);
TheParser.parse();
}
}

Congratulations! You’ve finished implementing the frontend for tinylang! You can use the example program, Gcd.mod, provided in the Defining a real programming language section to run the frontend:

$ tinylang Gcd.mod

Of course, this is a valid program, and it looks like nothing happens. Be sure to modify the file and provoke some error messages. We’ll continue with the fun in the next chapter by adding code generation.
Summary
In this chapter, you learned about the techniques that a real-world compiler uses in the frontend. Starting with the project layout, you created separate libraries for the lexer, the parser, and the semantic analyzer. To output messages to the user, you extended an existing LLVM class, allowing the messages to be stored centrally. The lexer is now separated into several interfaces.
Then, you learned how to construct a recursive descent parser from a grammar description, looked at what pitfalls to avoid, and learned how to use generators to do the job. The semantic analyzer you constructed performs all the semantic checks required by the language while being intertwined with the parser and AST construction.
The result of your coding effort is a fully decorated AST. You’ll use this in the next chapter to generate IR code and, finally, object code.s

Leave a Reply

Your email address will not be published. Required fields are marked *