Skip to content

Commit 4f239ae

Browse files
committed
Add static semantics to ensure proper variable declarations
1 parent c857eb4 commit 4f239ae

16 files changed

+257
-37
lines changed

.gitignore

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
.vscode
22
*.o
33
*.gch
4-
P2_test*.txt
4+
test*.txt
55
build
6-
frontEnd
6+
statSem

Makefile

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
#
88
# Source: https://spin.atomicobject.com/2016/08/26/makefile-c-projects/
99

10-
TARGET_EXEC ?= frontEnd
10+
TARGET_EXEC ?= statSem
1111

1212
BUILD_DIR ?= ./build
1313

README.md

+8
Original file line numberDiff line numberDiff line change
@@ -75,3 +75,11 @@ To edit import `assets/state-transition-table.csv` into your favorite spreadshee
7575
\<assign> -> **let** **Identifier** **=** \<expr>
7676

7777
\<O> -> **<** | **>** | **:**
78+
79+
## Static Semantics
80+
81+
The only static semantics imposed by the compiler are proper use of variables. Before using a variable, you must first declare it using the **var** keyword.
82+
83+
In our language scopes are imposed by blocks denoted by **start** and **end**, conditionals denoted by **if**, and loops denoted by **iter**.
84+
85+
For our compiler, we implement **local scoping** in contrast to global scoping.

backend-specification.pdf

417 KB
Binary file not shown.

src/main.cpp

+7-3
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#include <stdlib.h>
55
#include "timer.h"
66
#include "parser/parser.h"
7+
#include "semantic_analyzer/semantic_analyzer.h"
78

89
std::string get_filename(int argc, char** argv);
910

@@ -15,11 +16,14 @@ int main(int argc, char** argv)
1516
std::string filename = get_filename(argc, argv);
1617

1718
Parser parser(filename);
18-
Node* parseTree = parser.parse();
19+
Node* parse_tree = parser.parse();
1920

20-
Node::print(parseTree);
21+
SemanticAnalyzer semantic_analyzer = SemanticAnalyzer();
22+
Node::print(parse_tree);
23+
semantic_analyzer.analyze(parse_tree);
2124

22-
Node::destroy(parseTree);
25+
26+
Node::destroy(parse_tree);
2327

2428
timer_.stop("Timer stopped.");
2529

src/parser/node/node.cpp

+28-5
Original file line numberDiff line numberDiff line change
@@ -30,23 +30,46 @@ void Node::destroy(Node* node)
3030
delete node;
3131
}
3232

33-
std::ostream& operator<<(std::ostream &stream, const Node &node) {
34-
return stream << node.label;
35-
}
36-
3733
void Node::print(Node* node)
3834
{
3935
if (node == NULL) {
4036
return;
4137
}
4238

39+
print_node(node);
40+
41+
print_children(node);
42+
}
43+
44+
void Node::print_node(Node* node)
45+
{
4346
printf("%*c%d:%-9s ", node->level, ' ', node->level, node->label.c_str());
4447
for (auto i = node->tokens.begin(); i != node->tokens.end(); ++i)
4548
std::cout << *i << ' ';
4649
printf("\n");
50+
}
4751

52+
void Node::print_children(Node* node)
53+
{
4854
std::vector<Node*> children = node->children;
4955
for (unsigned int i = 0; i < children.size(); i++) {
5056
print(children.at(i));
5157
}
52-
}
58+
}
59+
60+
std::vector<Token> Node::get_identifier_tokens()
61+
{
62+
std::vector<Token> identifier_tokens;
63+
for (auto token : tokens) {
64+
if (token.is_identifier()) {
65+
identifier_tokens.push_back(token);
66+
}
67+
}
68+
return identifier_tokens;
69+
}
70+
71+
72+
std::ostream& operator<<(std::ostream &stream, const Node &node)
73+
{
74+
return stream << node.label;
75+
}

src/parser/node/node.h

+3
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,15 @@ class Node
1313
void append_child(Node* child);
1414
void append_token(Token token);
1515
static void print(Node* node);
16+
std::vector<Token> get_identifier_tokens();
1617
std::vector<Node*> children;
1718
std::string label;
1819
std::vector<Token> tokens;
1920
int level;
2021

2122
private:
23+
static void print_node(Node* node);
24+
static void print_children(Node* node);
2225
friend std::ostream& operator<<(std::ostream&, const Node&);
2326
};
2427

src/parser/parser.cpp

+25-22
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ Node* Parser::parse()
2121
if (token.is_eof()) {
2222
return root;
2323
}
24-
error();
24+
print_error_and_exit();
2525
}
2626

2727
/**
@@ -37,7 +37,7 @@ Node* Parser::S()
3737
node->append_child(block(level));
3838
return node;
3939
}
40-
error();
40+
print_error_and_exit();
4141
}
4242

4343
/**
@@ -47,7 +47,7 @@ Node* Parser::block(int level)
4747
{
4848
level++;
4949
if (KeywordToken::is_start_token(token)) {
50-
Node* node = Node::of("block", level);
50+
Node* node = Node::of(BLOCK, level);
5151
token = scanner->read();
5252
node->append_child(vars(level));
5353
node->append_child(stats(level));
@@ -56,7 +56,7 @@ Node* Parser::block(int level)
5656
return node;
5757
}
5858
}
59-
error();
59+
print_error_and_exit();
6060
}
6161

6262
/**
@@ -66,15 +66,15 @@ Node* Parser::vars(int level)
6666
{
6767
if (KeywordToken::is_var_token(token)) {
6868
level++;
69-
Node* node = Node::of("vars", level);
69+
Node* node = Node::of(VARS, level);
7070
token = scanner->read();
7171
if (token.is_identifier()) {
7272
node->append_token(token);
7373
token = scanner->read();
7474
node->append_child(vars(level));
7575
return node;
7676
} else {
77-
error();
77+
print_error_and_exit();
7878
}
7979
} else {
8080
return NULL;
@@ -156,7 +156,7 @@ Node* Parser::R(int level)
156156
token = scanner->read();
157157
return node;
158158
}
159-
error();
159+
print_error_and_exit();
160160
}
161161

162162
/**
@@ -186,7 +186,8 @@ Node* Parser::m_stat(int level)
186186
}
187187
}
188188

189-
bool Parser::is_first_of_stats(Token token) {
189+
bool Parser::is_first_of_stats(Token token)
190+
{
190191
return KeywordToken::is_read_token(token) ||
191192
KeywordToken::is_print_token(token) ||
192193
KeywordToken::is_start_token(token) ||
@@ -227,14 +228,15 @@ Node* Parser::stat(int level)
227228
check_for_comma_token();
228229
return node;
229230
}
230-
error();
231+
print_error_and_exit();
231232
}
232233

233-
void Parser::check_for_comma_token() {
234+
void Parser::check_for_comma_token()
235+
{
234236
if (DelimiterToken::is_comma_token(token)) {
235237
token = scanner->read();
236238
} else {
237-
error();
239+
print_error_and_exit();
238240
}
239241
}
240242

@@ -253,7 +255,7 @@ Node* Parser::in(int level)
253255
return node;
254256
}
255257
}
256-
error();
258+
print_error_and_exit();
257259
}
258260

259261
/**
@@ -268,7 +270,7 @@ Node* Parser::out(int level)
268270
node->append_child(expr(level));
269271
return node;
270272
}
271-
error();
273+
print_error_and_exit();
272274
}
273275

274276
/**
@@ -278,7 +280,7 @@ Node* Parser::ifstat(int level)
278280
{
279281
level++;
280282
if (KeywordToken::is_if_token(token)) {
281-
Node* node = Node::of("ifstat", level);
283+
Node* node = Node::of(IFSTAT, level);
282284
token = scanner->read();
283285
if (DelimiterToken::is_left_parentheses_token(token)) {
284286
token = scanner->read();
@@ -292,7 +294,7 @@ Node* Parser::ifstat(int level)
292294
}
293295
}
294296
}
295-
error();
297+
print_error_and_exit();
296298
}
297299

298300
/**
@@ -302,7 +304,7 @@ Node* Parser::loop(int level)
302304
{
303305
level++;
304306
if (KeywordToken::is_iter_token(token)) {
305-
Node* node = Node::of("loop", level);
307+
Node* node = Node::of(LOOP, level);
306308
token = scanner->read();
307309
if (DelimiterToken::is_left_parentheses_token(token)) {
308310
token = scanner->read();
@@ -316,7 +318,7 @@ Node* Parser::loop(int level)
316318
}
317319
}
318320
}
319-
error();
321+
print_error_and_exit();
320322
}
321323

322324
/**
@@ -338,7 +340,7 @@ Node* Parser::assign(int level)
338340
}
339341
}
340342
}
341-
error();
343+
print_error_and_exit();
342344
}
343345

344346
/**
@@ -353,17 +355,18 @@ Node* Parser::O(int level)
353355
token = scanner->read();
354356
return node;
355357
}
356-
error();
358+
print_error_and_exit();
357359
}
358360

359-
bool Parser::is_O_token(Token token) {
361+
bool Parser::is_O_token(Token token)
362+
{
360363
return OperatorToken::is_less_than_token(token) ||
361364
OperatorToken::is_greater_than_token(token) ||
362365
DelimiterToken::is_colon_token(token);
363366
}
364367

365-
void Parser::error()
368+
void Parser::print_error_and_exit()
366369
{
367-
std::cerr << "Parser error: " << token << std::endl;
370+
std::cerr << "Parser print_error_and_exit: " << token << std::endl;
368371
exit(1);
369372
}

src/parser/parser.h

+6-1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,11 @@
66
#include "../token/token.h"
77
#include "node/node.h"
88

9+
const std::string BLOCK = "block";
10+
const std::string IFSTAT = "ifstat";
11+
const std::string LOOP = "loop";
12+
const std::string VARS = "vars";
13+
914
class Parser
1015
{
1116
public:
@@ -34,7 +39,7 @@ class Parser
3439
Node* assign(int level);
3540
Node* O(int level);
3641
bool is_O_token(Token token);
37-
void error();
42+
void print_error_and_exit();
3843
void check_for_comma_token();
3944
};
4045

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
#include "semantic_analyzer.h"
2+
#include <iostream>
3+
4+
std::set<std::string> SemanticAnalyzer::new_scope_labels = {BLOCK, IFSTAT, LOOP};
5+
6+
7+
void SemanticAnalyzer::analyze(Node* parse_tree)
8+
{
9+
traverse(parse_tree);
10+
}
11+
12+
void SemanticAnalyzer::traverse(Node* node)
13+
{
14+
if (node == NULL) {
15+
return;
16+
}
17+
18+
if (introduces_new_scope(node)) {
19+
var_stack.push();
20+
}
21+
22+
check_for_variables(node);
23+
24+
if (introduces_new_scope(node)) {
25+
var_stack.pop();
26+
}
27+
28+
traverse_children(node);
29+
}
30+
31+
void SemanticAnalyzer::check_for_variables(Node* node)
32+
{
33+
std::vector<Token> id_tokens = node->get_identifier_tokens();
34+
35+
for (auto id_token : id_tokens) {
36+
if (contains_variable_declarations(node)) {
37+
var_stack.insert(id_token);
38+
} else {
39+
int location = var_stack.find(id_token);
40+
if (location == -1) {
41+
print_error_and_exit(id_token);
42+
}
43+
}
44+
}
45+
}
46+
47+
void SemanticAnalyzer::print_error_and_exit(Token token)
48+
{
49+
std::cerr << token << " undeclared. First use in this scope.\n";
50+
exit(EXIT_FAILURE);
51+
}
52+
53+
void SemanticAnalyzer::traverse_children(Node* node)
54+
{
55+
std::vector<Node*> children = node->children;
56+
for (unsigned int i = 0; i < children.size(); i++) {
57+
traverse(children.at(i));
58+
}
59+
}
60+
61+
bool SemanticAnalyzer::introduces_new_scope(Node* node)
62+
{
63+
return new_scope_labels.count(node->label) == 1;
64+
}
65+
66+
bool SemanticAnalyzer::contains_variable_declarations(Node* node)
67+
{
68+
return node->label == VARS;
69+
}

0 commit comments

Comments
 (0)