Move code into folders for convenience
This commit is contained in:
parent
914b93989c
commit
34e967f364
5
code/compiler/02/compile.sh
Executable file
5
code/compiler/02/compile.sh
Executable file
|
@ -0,0 +1,5 @@
|
||||||
|
bison -o parser.cpp -d parser.y
|
||||||
|
flex -o scanner.cpp scanner.l
|
||||||
|
g++ -c -o scanner.o scanner.cpp
|
||||||
|
g++ -c -o parser.o parser.cpp
|
||||||
|
g++ main.cpp parser.o scanner.o
|
|
@ -1,5 +1,5 @@
|
||||||
#include "compiler_ast.hpp"
|
#include "ast.hpp"
|
||||||
#include "compiler_parser.hpp"
|
#include "parser.hpp"
|
||||||
|
|
||||||
void yy::parser::error(const std::string& msg) {
|
void yy::parser::error(const std::string& msg) {
|
||||||
std::cout << "An error occured: " << std::endl;
|
std::cout << "An error occured: " << std::endl;
|
|
@ -1,8 +1,8 @@
|
||||||
%{
|
%{
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include "compiler_ast.hpp"
|
#include "ast.hpp"
|
||||||
#include "compiler_parser.hpp"
|
#include "parser.hpp"
|
||||||
|
|
||||||
std::vector<definition_ptr> program;
|
std::vector<definition_ptr> program;
|
||||||
extern yy::parser::symbol_type yylex();
|
extern yy::parser::symbol_type yylex();
|
|
@ -2,8 +2,8 @@
|
||||||
|
|
||||||
%{
|
%{
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include "compiler_ast.hpp"
|
#include "ast.hpp"
|
||||||
#include "compiler_parser.hpp"
|
#include "parser.hpp"
|
||||||
|
|
||||||
#define YY_DECL yy::parser::symbol_type yylex()
|
#define YY_DECL yy::parser::symbol_type yylex()
|
||||||
|
|
|
@ -201,7 +201,7 @@ So, our two regular expressions will be `[a-z][a-zA-Z]*` for the lowercase varia
|
||||||
this, we create a new file, `scanner.l`, in which we write a mix of regular expressions
|
this, we create a new file, `scanner.l`, in which we write a mix of regular expressions
|
||||||
and C++ code. Here's the whole thing:
|
and C++ code. Here's the whole thing:
|
||||||
|
|
||||||
{{< rawblock "compiler_scanner.l" >}}
|
{{< rawblock "compiler/01/scanner.l" >}}
|
||||||
|
|
||||||
A flex file starts with options. I set the `noyywrap` option, which disables a particular
|
A flex file starts with options. I set the `noyywrap` option, which disables a particular
|
||||||
feature of flex that we won't use, and which causes linker errors. Next up,
|
feature of flex that we won't use, and which causes linker errors. Next up,
|
||||||
|
|
|
@ -225,14 +225,14 @@ Just like with tokenizing, there exists a piece of software that will generate a
|
||||||
It's called Bison, and it is frequently used with Flex. Before we get to bison, though, we need to pay a debt we've already
|
It's called Bison, and it is frequently used with Flex. Before we get to bison, though, we need to pay a debt we've already
|
||||||
incurred - the implementation of our AST. Such a tree is language-specific, so Bison doesn't generate it for us. Here's what
|
incurred - the implementation of our AST. Such a tree is language-specific, so Bison doesn't generate it for us. Here's what
|
||||||
I came up with:
|
I came up with:
|
||||||
{{< codeblock "C++" "compiler_ast.hpp" >}}
|
{{< codeblock "C++" "compiler/02/ast.hpp" >}}
|
||||||
We create a base class for an expression tree, which we call `ast`. Then, for each possible syntactic construct in our language
|
We create a base class for an expression tree, which we call `ast`. Then, for each possible syntactic construct in our language
|
||||||
(a number, a variable, a binary operation, a case expression) we create a subclass of `ast`. The `ast_case` subclass
|
(a number, a variable, a binary operation, a case expression) we create a subclass of `ast`. The `ast_case` subclass
|
||||||
is the most complex, since it must contain a list of case expression branches, which are a combination of a `pattern` and
|
is the most complex, since it must contain a list of case expression branches, which are a combination of a `pattern` and
|
||||||
another expression.
|
another expression.
|
||||||
|
|
||||||
Finally, we get to writing our Bison file, `parser.y`. Here's what I come up with:
|
Finally, we get to writing our Bison file, `parser.y`. Here's what I come up with:
|
||||||
{{< rawblock "compiler_parser.y" >}}
|
{{< rawblock "compiler/02/parser.y" >}}
|
||||||
|
|
||||||
There's a few things to note here. First of all, the __parser__ is the "source of truth" regarding what tokens exist in our language.
|
There's a few things to note here. First of all, the __parser__ is the "source of truth" regarding what tokens exist in our language.
|
||||||
We have a list of `%token` declarations, each of which corresponds to a regular expression in our scanner.
|
We have a list of `%token` declarations, each of which corresponds to a regular expression in our scanner.
|
||||||
|
@ -260,7 +260,7 @@ to return `yy::parser::symbol_type`. You can see it in our forward declaration o
|
||||||
|
|
||||||
Now that we made these changes, it's time to hook up Flex to all this. Here's a new version
|
Now that we made these changes, it's time to hook up Flex to all this. Here's a new version
|
||||||
of the Flex scanner, with all necessary modifications:
|
of the Flex scanner, with all necessary modifications:
|
||||||
{{< rawblock "compiler_scanner_bison.l" >}}
|
{{< rawblock "compiler/02/scanner.l" >}}
|
||||||
|
|
||||||
The key two ideas are that we overrode the default signature of `yylex` by changing the
|
The key two ideas are that we overrode the default signature of `yylex` by changing the
|
||||||
`YY_DECL` preprocessor variable, and used the `yy::parser::make_<TOKEN>` functions
|
`YY_DECL` preprocessor variable, and used the `yy::parser::make_<TOKEN>` functions
|
||||||
|
@ -268,16 +268,10 @@ to return the `symbol_type` rather than `int`.
|
||||||
|
|
||||||
Finally, let's get a main function so that we can at least check for segmentation faults
|
Finally, let's get a main function so that we can at least check for segmentation faults
|
||||||
and other obvious mistakes:
|
and other obvious mistakes:
|
||||||
{{< codeblock "C++" "compiler_main.cpp" >}}
|
{{< codeblock "C++" "compiler/02/main.cpp" >}}
|
||||||
|
|
||||||
Now, we can compile and run the code:
|
Now, we can compile and run the code:
|
||||||
```
|
{{< codeblock "Bash" "compiler/02/compile.sh" >}}
|
||||||
flex -o compiler_scanner.cpp compiler_scanner_bison.l
|
|
||||||
bison -o compiler_parser.cpp -d compiler_parser.y
|
|
||||||
g++ -c -o scanner.o compiler_scanner.cpp
|
|
||||||
g++ -c -o parser.o compiler_parser.cpp
|
|
||||||
g++ compiler_main.cpp parser.o scanner.o
|
|
||||||
```
|
|
||||||
We used the `-d` option for Bison to generate the `compiler_parser.hpp` header file,
|
We used the `-d` option for Bison to generate the `compiler_parser.hpp` header file,
|
||||||
which exports our token declarations and token creation functions, allowing
|
which exports our token declarations and token creation functions, allowing
|
||||||
us to use them in Flex.
|
us to use them in Flex.
|
||||||
|
|
Loading…
Reference in New Issue
Block a user