Purpose
`fb.bas`: Main module for the compiler, parent module for parser/lexer/AST/IR/emitters, toplevel file & include file handling
`parser*.bas`: Parsing/compilation functions: lexer tokens -> AST nodes.
`symb*.bas`: Symbol tables and lookup, namespace/scope handling.
`rtl*.bas`: Helpers to build AST calls to rtlib/gfxlib functions.
The structure of the parser has a very close relation to the FreeBASIC grammar. Basically there is a parsing function for every element of the grammar.
The parser retrieves tokens from the lexer and validates the input source code. Most error messages (besides command line and file access errors) come from here. Additionally the parser functions build up the corresponding AST. This is the heart of the compilation process.
Many of the parser's (or rather compiler's) functions (prefixed with a 'c') parse and skip the grammar element they represent, or show an error if they don't find it. The parser is fairly recursive, mostly because of the expression parser and the #include parsing.
From parsing to emitting
When parsing code a corresponding AST is built up to represent the program. The AST is used to represent executable code, but also to hold temporary expressions, for example the values of constants or the initializers found while parsing type or procedure declarations. The AST does not contain nodes for code flow constructs like IF, DO/LOOP, GOTO, RETURN, EXIT DO, etc., but it contains labels and branches. Likewise, several operations (like IIF(), ANDALSO, ORELSE, field dereference, member access) are replaced by the corresponding set of lower-level operations in the AST.
After parsing a function, the AST for this function is optimized, and then emitted recursively via `astLoad*()` calls on each node, from the top down. Note that each AST node has its own implementation of `astLoad()`.