Components
evc.c
evc.c
is the main source file for the compiler. It handles the command
line parsing and executes the lower level functions of the compiler accordingly.
These lower level functions are:
- Opening and reading from file input
- Parsing the input and build an abstract syntax tree (AST)
- Link references to build the scopes
- Validate the AST for correctness
- Generate LLVM-IR or LLVM bytecode (if output file is given)
compiler.(c|h)
The files compiler.(c|h)
handles the parsing of the input and creation of
the AST. This is done by initializing the lexer with the input string and
passing every token that is generated by the lexer to the parser. The parser
then generates the AST based on it's grammar.
The return is an AST.
lexer.l
lexer.l
is the specification for the lexer generator
Flex, which is invoked to create C source code
for a lexer that generates all necessary token for the parser from the input.
The specification consists of some configuration for the lexer generator and all strings that should be matched as single tokens. Whitespace is ignored and code comments are skipped.
parser.y
parser.y
is the specification for
The LEMON Parser Generator which is used to
generate a parser from the grammar of the Event language.
Each grammar rule consists of left and a right side. The left side represents
the name of the generated node in the AST. the right side is a composition of
components needed to generate that node. Based on the given components the body
of the rule executes code. Mostly a new node is created by
tree_create_node()
from the
LibCollect. The components are
integrated into the new node to build the whole AST. Some rules create scopes
for the validation and code generation. Take a look at
Scopes for more information.
parser_signatures.h
parser_signatures.h
contains the prototypes of the generated parser
functions. LEMON does not generate them itself because it doesn't know which
data types the tokens will have.
parser_state.h
parser_state.h
contains the state of the parser at any time. It also holds
a reference to the generated AST for easier handling inside the compiler.
ast.(c|h)
The files ast.(c|h)
define which information the AST is holding. For that,
a struct payload
is defined which is inserted into a new tree node. The
payload contains following information:
- The type of the node (e.g. N_RULE_DECLARATION)
- The alternative to represent the type of the nodes children (e.g. ALT_IDENTIFIER)
- Custom data depending on the nodes type
The custom data can hold:
- Simple data (e.g. string, int, ...)
- Scopes
- References to other nodes
scope.(c|h)
The files scope.(c|h)
provide functions to create the scopes and references
from the information in the AST. The most important function is
link_references()
. It iterates over the tree and visits every node. For
every node it resolves necessary references by search the tree in parent
direction until it founds the valid node. That scoping is later used by the
validator and the code generator.
validator.(c|h)
validator.(c|h)
is used to check if the AST and it's scopes and references
are created according to our language definition. For that it provides a single
function validate()
that iterates over the tree and perform the necessary
checks for every node. The checks are more or less complicated. Take a look into
the comments of the source code to learn more about the validation.
codegen.(c|h)
codegen.(c|h)
generates an LLVM module from the AST. The function
generate_module()
iterates over all declarations and recursively generates
the necessary LLVM-IR for every sub-tree node.
operators.(c|h)
The files operators.(c|h)
constitute the standard library for the
Event language. It has to be shipped with a compiled Event program.
The following operators are implemented:
op_v_eq_v()
: vector equals vectorop_v_neq_v()
: vector not equals vectorop_v_add_v()
: add vector to vectorop_v_sub_v()
: subtract vector from vectorop_s_mult_v()
: multiply a scalar with a vectorop_v_lt_v()
: vector less than vector for every componentop_v_gt_v()
: vector greater than vector for every componentop_i_eq_i()
: integer equals integer