Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

General tweaks & actual code generation for int f() { return 5; } #7

Merged
merged 42 commits into from
Feb 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
d2e55a3
gitignore: Ignore code coverage output
Jpnock Jan 29, 2024
df20f1c
gitignore: Ignore compiler dependency files
Jpnock Jan 29, 2024
b8ff059
docker: Install lcov dependency
Jpnock Jan 29, 2024
72e4138
Correct CPPFLAGS to CXXFLAGS
Jpnock Jan 29, 2024
3d61dc2
make: Clean more generated files
Jpnock Jan 29, 2024
02786d5
docs: General tweaks
Jpnock Jan 29, 2024
801d284
test: Exit early if make fails
Jpnock Jan 30, 2024
da69108
test: Preserve assembly from toolchain test
Jpnock Jan 30, 2024
9e85b13
test: Improve output and logging of the test script
Jpnock Jan 30, 2024
1751376
ast: Add a pretty print method for visualising the AST
Jpnock Jan 30, 2024
fe9b2d3
Improve clarity of CLI arg parsing & output to pretty print file
Jpnock Jan 30, 2024
1a0306b
docs: Update
Jpnock Jan 30, 2024
ecc15c2
Tidy grammar
Jpnock Jan 30, 2024
2156b5e
make: Tidy Makefile
Jpnock Jan 30, 2024
1a572e4
ast: Prevent nullptr deref in FunctionDefinition when emitting body
Jpnock Jan 30, 2024
4546d60
Enable ASAN for easier debugging
Jpnock Jan 30, 2024
039110a
test: Tidy output of test script
Jpnock Jan 30, 2024
a72b5a0
Delete duplicate seen test
Jpnock Jan 30, 2024
9e55f18
docker: Add nano for terminal commands that need a text editor
Jpnock Jan 30, 2024
71fb1f8
Implement support for `return INT_CONSTANT;` statements
Jpnock Jan 30, 2024
c5bbf80
Simplify the grammar to the minimum example for `int f() { return 5; }`
Jpnock Jan 30, 2024
f4d9999
Rename the full grammar example
Jpnock Jan 30, 2024
90c8d4b
Add docs explaining the grammar files provided
Jpnock Jan 30, 2024
0455859
Change the example test back to `return 5;`
Jpnock Jan 30, 2024
61c4efc
test: Add 15 second timeout to each stage of testing to prevent one h…
Jpnock Jan 30, 2024
13fa148
Improve readability of FunctionDefinition
Jpnock Jan 30, 2024
5e78daa
Avoid use of `branches` in Node as it hinders readability
Jpnock Jan 30, 2024
bc0b229
Add a helper class, NodeList, and demonstrate its usage
Jpnock Jan 30, 2024
38242ed
Improve Context docs
Jpnock Jan 30, 2024
1feca4e
Emit TODOs to stderr
Jpnock Jan 30, 2024
8f1f1d9
Fix running the coverage command via test.sh
Jpnock Jan 30, 2024
033870e
docs: Add instructions on how to view the coverage webpage
Jpnock Jan 30, 2024
bc1b25d
Refactor compiler main()
Jpnock Jan 31, 2024
7c93acf
Enable coverage via env-var
Jpnock Jan 31, 2024
7cc832c
Remove WIP comment
Jpnock Jan 31, 2024
fe35d61
Comply with Google C++ Style Guide naming conventions
Jpnock Jan 31, 2024
9458ef7
Separate headers and implementation
Jpnock Jan 31, 2024
7fd6bd6
Added style guide suggestions for contributors
Jpnock Jan 31, 2024
730ca0a
Update style guide
Jpnock Jan 31, 2024
61503c7
Remove not implemented warnings
Jpnock Jan 31, 2024
7cd8fab
Rename ast_statements to ast_jump_statement
Jpnock Jan 31, 2024
1034f7b
test: Don't run `make clean` if DONT_CLEAN=1
Jpnock Jan 31, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,6 @@ bin/
*.tab.cpp
*.yy.cpp
*.output
*.gcno
coverage/
*.d
13 changes: 13 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Contributing

## Style Guides

For C++ code, try to follow the [Google naming style guide](https://google.github.io/styleguide/cppguide.html#Function_Names) where possible. This will allow for consistency across the code base.

These can be simplified to:

- Class data members should be snake_case_ and end with a underscore (note: this doesn't apply to class members)
- Using this convention prevents the need to use `this->` when accessing class data members since it is obvious from the trailing underscore
- Function names should be PascalCase
- Variable names should be snake_case
- Parameter name should be snake_case
4 changes: 3 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@ RUN apt-get update && apt-get install -y --fix-missing \
build-essential \
ca-certificates \
curl \
device-tree-compiler
device-tree-compiler \
lcov \
nano

# Install RISC-V Toolchain
WORKDIR /tmp
Expand Down
33 changes: 19 additions & 14 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,30 +1,34 @@
CPPFLAGS += -std=c++20 -W -Wall -g -Wno-unused-parameter -Wno-unused-variable -Wno-unused-function -I include
# Based on https://stackoverflow.com/a/52036564 which is well worth reading!

CPPFILES := $(wildcard src/*.cpp)
DEPENDENCIES := $(patsubst %.cpp,%.d,$(CPPFILES))
-include $(DEPENDENCIES)
OBJFILES := $(patsubst %.cpp,%.o,$(CPPFILES))
OBJFILES += src/lexer.yy.o src/parser.tab.o
CXXFLAGS += -std=c++20 -W -Wall -g -Wno-unused-parameter -Wno-unused-variable -Wno-unused-function -fsanitize=address -static-libasan -O0 -rdynamic -I include

SOURCES := $(wildcard src/*.cpp)
DEPENDENCIES := $(patsubst %.cpp,%.d,$(SOURCES))

OBJECTS := $(patsubst %.cpp,%.o,$(SOURCES))
OBJECTS += src/parser.tab.o src/lexer.yy.o


.PHONY: default clean with_coverage coverage

default: bin/c_compiler

bin/c_compiler : $(OBJFILES)
bin/c_compiler: $(OBJECTS)
@mkdir -p bin
g++ $(CPPFLAGS) -o $@ $^
g++ $(CXXFLAGS) -o $@ $^

-include $(DEPENDENCIES)

%.o: %.cpp Makefile
g++ $(CPPFLAGS) -MMD -MP -c $< -o $@
g++ $(CXXFLAGS) -MMD -MP -c $< -o $@

src/parser.tab.cpp src/parser.tab.hpp: src/parser.y
bison -v -d src/parser.y -o src/parser.tab.cpp

src/lexer.yy.cpp : src/lexer.flex src/parser.tab.hpp
flex -o src/lexer.yy.cpp src/lexer.flex

with_coverage : CPPFLAGS += --coverage
with_coverage : CXXFLAGS += --coverage
with_coverage : bin/c_compiler

coverage : coverage/index.html
Expand All @@ -38,11 +42,12 @@ coverage/index.html :
@find . -name "*.gcda" -delete

clean :
@rm -rf coverage
@find . -name "*.o" -delete
@rm -rf bin/*
@rm -rf coverage/
@rm -rf src/*.o
@rm -rf src/*.d
@rm -rf src/*.gcno
@rm -rf bin/
@rm -f src/*.tab.hpp
@rm -f src/*.tab.cpp
@rm -f src/*.yy.cpp
@rm -f src/*.output

2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ Changelog
* Added a "Getting started" guide and incorporated last year's feedback from Ed.
* Changed the 10% of the grade (previously only for time management) to also account for code design to reward thoughtful planning.
* Improved the skeleton compiler to be more advanced by integrating lexer and parser to hopefully jump-start progress and avoid unnecessary debugging.
* Covered assembler directives in more details by showcasing the meaning behind an example assembly program, because that topic had always caused confusion in the past years. [WIP]
* Covered assembler directives in more details by showcasing the meaning behind an example assembly program, because that topic had always caused confusion in the past years.

* New for 2022/2023:

Expand Down
2 changes: 1 addition & 1 deletion compiler_tests/_example/example.c
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
int f()
{
return;
return 5;
}
4 changes: 0 additions & 4 deletions compiler_tests/local_var/return_constant.c

This file was deleted.

7 changes: 0 additions & 7 deletions compiler_tests/local_var/return_constant_driver.c

This file was deleted.

16 changes: 11 additions & 5 deletions docs/basic_compiler.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
Basic compiler
==============

For the first time ever, you are provided with a basic compiler that can lex, parse and generate (incorrect) code for the following program:
For the first time ever, you are provided with a basic compiler that can lex, parse and generate code for only the following program:
```
int f() {
return;
int f()
{
return 5;
}
```

The output assembly is hardcoded, so that the basic compiler passes one of the provided test cases. However, having a functioning compiler should allow you to hopefully jump-start the development of the actually interesting parts of this coursework while avoiding the common early pitfalls that students have faced in previous years. It should also allow you to better understand the underlying C90 grammar and have an easier time when adding new features.
Having a somewhat functioning compiler should allow you to get started with development of the interesting parts of this coursework while avoiding the common early pitfalls that students have faced in previous years. It should also allow you to better understand the underlying C90 grammar and have an easier time adding new features.

The provided basic compiler is able to traverse the following AST related to the above program. In order to expand its capabilities, you should develop the parser and the corresponding code generation at the same time - do not try to fully implement one before the other.

Expand All @@ -17,6 +18,11 @@ The provided basic compiler is able to traverse the following AST related to the

The lexer and parser are loosely based on the "official" grammar covered [here](https://www.lysator.liu.se/c/ANSI-C-grammar-l.html) and [here](https://www.lysator.liu.se/c/ANSI-C-grammar-y.html) respectively. While they should suffice for a significant portions of features, you might need to improve them to implement the more advanced ones. If you find the grammar too complicated to understand, it is also perfectly fine to create your own simple grammar and build upon it as you add more features.

You can follow the patterns introduced for the code generation part of the basic compiler, but you might find adjusting them to your needs be better in the long run. You are recommended to follow the coding style that best suits you while hopefully picking strong design skills throughout the development of your compiler.
Two versions of the grammar have been provided:

- [parser.y](../src/parser.y) contains a stripped down version of the grammar which contains only the elements from the full grammar that are necessary to parse the above program. **This is the grammar used when you run ./test.sh**.
- [parser_full.y.example](../src/parser_full.y.example) is the same as `parser.y` but also contains the rest of the C90 grammar not used in the above program. Once you understand how the stripped version works, you have two options: you can extend `parser.y` at your own pace and add pieces of the C90 grammar to it as you implement them; alternatively, you may paste the contents of `parser_full.y.example` into `parser.y` allowing you to parse everything but this may make the file much harder to navigate.

Note that you don't have to use any of the provided grammar examples (or even flex and bison) if you are comfortable with doing something else. However, be warned that TAs will likely only be able to provided limited support for such alternatives.

You can follow the patterns introduced for the code generation part of the basic compiler, but you might find adjusting them to your needs be better in the long run. You are recommended to follow the coding style that best suits you while hopefully picking strong design skills throughout the development of your compiler.
4 changes: 3 additions & 1 deletion docs/c_compiler.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,9 @@ The compilation function is invoked using the flag `-S`, with the source file an

bin/c_compiler -S [source-file.c] -o [dest-file.s]

You can assume that the command-line arguments will always be in this order, and that there will be no spaces in source or destination paths.
You can assume that the command-line (CLI) arguments will always be in this order, and that there will be no spaces in source or destination paths.

NOTE: the provided starting point in this repository already functions as specified above, so these CLI arguments should work out of the box (unless you decide not to use the provided base compiler).

Input
-----
Expand Down
16 changes: 15 additions & 1 deletion docs/coverage.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,18 @@ This will generate a webpage `coverage/index.html` with a listing of all the sou

![Index.html screenshot](./coverage_example.png)

It can also be used automatically on all test files by running `./test.sh coverage`.
It can also be used automatically on all test files by running `COVERAGE=1 ./test.sh`.

## Viewing the coverage webpage

You can view the webpage in your browser by navigating to the coverage directory and running the following command:

```python3 -m http.server```

For example:

```console
user@host:/workspaces/langproc-cw# cd coverage
user@host:/workspaces/langproc-cw/coverage# python3 -m http.server
Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ...
```
33 changes: 26 additions & 7 deletions docs/environment_guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,27 @@ Many students develop their compiler in VS Code, as this has good support for co
1) Install [Docker Desktop](https://www.docker.com/products/docker-desktop/). If you are on Apple M1/M2, make sure to choose the Apple Silicon download.
2) Open VS Code and install the [Dev Containers](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers) extension.
3) Open the top folder of your repo (`/langproc-cw-.../`), in VS Code.
4) Open the Command Palette in VS Code. You can do this by the shortcut `Ctrl + Shift + P` on Windows or `Cmd + Shift + P` on Mac. Alternatively, you can access this from `View -> Command Palette`.
4) Open the Command Palette in VS Code:
- Windows: `Ctrl + Shift + P`
- Mac OS: `Cmd + Shift + P`
- Alternatively, you can access this from the menu bar `View -> Command Palette`.
5) Enter `>Dev Containers: Reopen in Container` into the Command Palette.
6) After a delay -- depending on how fast your Internet connection can download ~1GB -- you will now be in the container environment. For those interested, VS Code reads the container configuration from the [.devcontainer/devcontainer.json](.devcontainer/devcontainer.json) file.
6) After a delay you will now be in the container environment.
- The delay will vary based on how fast you can download around 1GB over your Internet connection.
- For those interested, VS Code reads the container configuration from the [.devcontainer/devcontainer.json](.devcontainer/devcontainer.json) file.
7) Test that your tools are correctly setup by running `./scripts/toolchain_test.sh` in the VS Code terminal, accessible via `Terminal -> New Terminal`. Your output should look as follows:

```console
root@e3221f21a2a1:/workspaces/langproc-env# ./scripts/toolchain_test.sh

g++ -std=c++20 -W -Wall -g -I include -o bin/c_compiler src/cli.cpp src/compiler.cpp
[...]

Compiling: compiler_tests/_example/example.c
Parsing: compiler_tests/_example/example.c
AST parsing complete
Printing parsed AST...
Printed parsed AST to: bin/riscv_example.s.printed
Compiling parsed AST...
Compiled to: bin/riscv_example.s

bbl loader
Hello from RISC-V
Test function produced value: 8.700000
Expand All @@ -40,8 +48,13 @@ Many students develop their compiler in VS Code, as this has good support for co

1) Install [Docker](https://www.docker.com/products/docker-desktop/). If you are on Apple M1/M2, make sure to choose the Apple Silicon download.
2) Open a terminal (Powershell on Windows; Terminal on Mac) to the folder containing this file.
3) Inside that terminal, run `docker build -t compilers_image .`
4) Once that completes, run `docker run --rm -it -v "${PWD}:/code" -w "/code" --name "compilers_env" compilers_image`.
3) Inside that terminal, run `docker build -t compilers_image .` to build the Docker container image
4) Once that completes, run the following command to start the Docker container

```bash
docker run --rm -it -v "${PWD}:/code" -w "/code" --name "compilers_env" compilers_image
```

5) You should now be inside the LangProc tools container, where you can run `./scripts/toolchain_test.sh` inside the `/code` folder to check that your tools are working correctly. Note that the folder containing this file, as well as any subdirectories, are mounted inside this container under the path `/code`. The output of running the command should look as follows:


Expand All @@ -53,6 +66,12 @@ Many students develop their compiler in VS Code, as this has good support for co
Compiling: compiler_tests/_example/example.c
Compiled to: bin/riscv_example.s

Parsing: compiler_tests/_example/example.c
AST parsing complete
Printing parsed AST...
Printed parsed AST to: bin/riscv_example.s.printed
Compiling parsed AST...
Compiled to: bin/riscv_example.s
bbl loader
Hello from RISC-V
Test function produced value: 8.700000
Expand Down
4 changes: 3 additions & 1 deletion include/ast.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@
#include "ast_jump_statement.hpp"
#include "ast_node.hpp"
#include "ast_type_specifier.hpp"
#include "ast_constant.hpp"
#include "ast_context.hpp"

extern Node* parseAST(std::string file_name);
extern Node *ParseAST(std::string file_name);

#endif
18 changes: 18 additions & 0 deletions include/ast_constant.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#ifndef AST_CONSTANT_HPP
#define AST_CONSTANT_HPP

#include "ast_node.hpp"

class IntConstant : public Node
{
private:
int value_;

public:
IntConstant(int value) : value_(value) {}

void EmitRISC(std::ostream &stream, Context &context) const override;
void Print(std::ostream &stream) const override;
};

#endif
13 changes: 8 additions & 5 deletions include/ast_context.hpp
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
#ifndef AST_CONTEXT
#define AST_CONTEXT
#ifndef AST_CONTEXT_HPP
#define AST_CONTEXT_HPP

// An object of class Context is passed between AST nodes during compilation to provide adequate context
class Context {
/* TODO decide what goes inside here */
// An object of class Context is passed between AST nodes during compilation.
// This can be used to pass around information about what's currently being
// compiled (e.g. function scope and variable names).
class Context
{
/* TODO decide what goes inside here */
};

#endif
20 changes: 14 additions & 6 deletions include/ast_direct_declarator.hpp
Original file line number Diff line number Diff line change
@@ -1,13 +1,21 @@
#ifndef AST_DIRECT_DECLARATOR
#define AST_DIRECT_DECLARATOR
#ifndef AST_DIRECT_DECLARATOR_HPP
#define AST_DIRECT_DECLARATOR_HPP

#include "ast_node.hpp"

class DirectDeclarator : public Node {
class DirectDeclarator : public Node
{
private:
Node *identifier_;

public:
DirectDeclarator(Node* identifier);
~DirectDeclarator() {};
void emitRISC(std::ostream &stream, Context &context) const;
DirectDeclarator(Node *identifier) : identifier_(identifier){};
~DirectDeclarator()
{
delete identifier_;
};
void EmitRISC(std::ostream &stream, Context &context) const override;
void Print(std::ostream &stream) const override;
};

#endif
20 changes: 16 additions & 4 deletions include/ast_function_definition.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,23 @@

#include "ast_node.hpp"

class FunctionDefinition : public Node {
class FunctionDefinition : public Node
{
private:
Node *declaration_specifiers_;
Node *declarator_;
Node *compound_statement_;

public:
FunctionDefinition(Node* declaration_specifiers, Node* declarator, Node* compound_statement);
~FunctionDefinition() {};
void emitRISC(std::ostream &stream, Context &context) const;
FunctionDefinition(Node *declaration_specifiers, Node *declarator, Node *compound_statement) : declaration_specifiers_(declaration_specifiers), declarator_(declarator), compound_statement_(compound_statement){};
~FunctionDefinition()
{
delete declaration_specifiers_;
delete declarator_;
delete compound_statement_;
};
void EmitRISC(std::ostream &stream, Context &context) const override;
void Print(std::ostream &stream) const override;
};

#endif
17 changes: 10 additions & 7 deletions include/ast_identifier.hpp
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
#ifndef AST_IDENTIFIER
#define AST_IDENTIFIER
#ifndef AST_IDENTIFIER_HPP
#define AST_IDENTIFIER_HPP

#include "ast_node.hpp"

class Identifier : public Node {
class Identifier : public Node
{
private:
std::string* identifier;
std::string identifier_;

public:
Identifier(std::string* _identifier) : identifier(_identifier) {};
~Identifier() {delete identifier;};
void emitRISC(std::ostream &stream, Context &context) const;
Identifier(std::string identifier) : identifier_(identifier){};
~Identifier(){};
void EmitRISC(std::ostream &stream, Context &context) const override;
void Print(std::ostream &stream) const override;
};

#endif
Loading
Loading