-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathParser.cs
More file actions
194 lines (167 loc) · 7.81 KB
/
Parser.cs
File metadata and controls
194 lines (167 loc) · 7.81 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using System.Text;
using System.Threading.Tasks;
using static PasCompiler.Token;
using static PasCompiler.Token.E;
namespace PasCompiler {
class Parser {
public Parser(string source) {
_lexer = new Lexer(source);
}
// Property to get next token
Token NextToken() {
_currToken = _lexer.nextToken();
return _currToken;
}
//Token NextToken => _lexer.nextToken ();
// Property that peeks the next token
Token PeekToken()
=> _lexer.peekToken();
// Throw error if the kind of the next token isn't what we expect
void ExpectTokenKind(E kind) {
if (NextToken().Kind != kind) throw new Exception($"Expected {kind} token");
}
// Throw exception if the value of the next token isn't what we expect
void ExpectTokenVal(string value) {
if (NextToken().Value != value) throw new Exception($"Expected '{value}' token");
}
// Top level routine that parses the source text and returns an abstract syntax tree representing the program
public ASTNode Parse() {
ParseProgramHeader();
BlockNode block = ParseBlock();
return new ProgramNode(block);
}
void ParseProgramHeader() {
ExpectTokenVal("program"); // First token must be the "program" keyword
ExpectTokenKind(Identifier); // The second token must be the name of the program: an identifier
ExpectTokenKind(SemiColon); // Program definition ends with a semicolon
}
BlockNode ParseBlock() {
List<VarDecl> varDeclarations = ParseVarDecl();
CompoundStatementNode statementList = ParseCompoundStatement();
return new BlockNode(varDeclarations, statementList);
}
List<VarDecl> ParseVarDecl() {
if (PeekToken().Value != "var") return null; // Variable declatation section is empty (no variables declared)
// Otherwise, we do have variable declarations
List<VarDecl> varDeclarations = new List<VarDecl>(); // Final list of variable declarations
List<string> varNamesPerGrouping = new List<string>(); // List of variable names declared together in a single grouping
NextToken(); // Skip the var token
// Parse the variable declarations one line at a time
while (PeekToken().Kind == Identifier) {
AddVarDeclarationsPerGroup();
ExpectTokenKind(SemiColon);
}
return varDeclarations;
// Helper method that creates VarDecl nodes for variables declared in a group.
// A group declaration is (ident1, indent2, ... , identn : Type;) as opposed to a single variable declaration (ident : Type;)
void AddVarDeclarationsPerGroup() {
while (true) {
ExpectTokenKind(Identifier);
varNamesPerGrouping.Add(_currToken.Value);
if (PeekToken().Kind == Colon) break;
ExpectTokenKind(Comma);
}
NextToken(); // We exit the loop once we peek a Colon (':'). So, skip past the colon
ExpectTokenKind(BasicType); // The token after the colon is the type of the declared variables
// Create a VarDecl node for each seperate variable declared in this group
foreach (string varName in varNamesPerGrouping) {
varDeclarations.Add(new VarDecl(new IdentNode(varName), _currToken.Value));
}
varNamesPerGrouping.Clear(); // Clear the list of variable names
}
}
StatementNode ParseStatement() {
StatementNode statement;
Token next = PeekToken();
if (next.Kind == Identifier) // Could also be method calls (To be added later)
statement = ParseAssignmentStatement();
else if (next.Value == "if")
statement = ParseConditional();
else if (next.Value == "begin")
statement = ParseCompoundStatement();
else throw new Exception($"Unexpected {next} token");
return statement;
}
CompoundStatementNode ParseCompoundStatement() {
ExpectTokenVal("begin"); // Compound statements start with the 'begin' keyword
List<StatementNode> statementList = new List<StatementNode>();
while (PeekToken().Value != "end")
statementList.Add(ParseStatement());
NextToken(); // Skip the "end" token
ExpectTokenKind(Period);
return new CompoundStatementNode(statementList);
}
ConditionalStatementNode ParseConditional() {
NextToken(); // Skip past the if
ExpressionNode condition = ParseExpression();
ExpectTokenVal("then");
StatementNode ifBody = ParseStatement();
StatementNode elseBody = null;
if (PeekToken().Value == "else") {
NextToken();
elseBody = ParseStatement();
}
return new ConditionalStatementNode(condition, ifBody, elseBody);
}
AssignmentNode ParseAssignmentStatement() {
IdentNode identNode = new IdentNode(NextToken().Value); // Get the identifier
ExpectTokenKind(Assignment);
ExpressionNode expNode = ParseExpression();
ExpectTokenKind(SemiColon);
return new AssignmentNode(identNode, expNode);
}
ExpressionNode ParseExpression() {
ExpressionNode expr = ParseNonConditionalExpression();
if (PeekToken().IsConiditionalOperator) {
expr = new BinaryNode(NextToken(), expr, ParseNonConditionalExpression());
}
return expr;
}
ExpressionNode ParseNonConditionalExpression() {
ExpressionNode expr = Term();
while (PeekToken().IsAdditiveOperator) {
expr = new BinaryNode(NextToken(), expr, Term());
}
return expr;
ExpressionNode Term() {
ExpressionNode term = Factor();
while (PeekToken().IsMultiplicativeOperator) {
term = new BinaryNode(NextToken(), term, Factor());
}
return term;
}
ExpressionNode Factor() {
switch (NextToken().Kind) {
case OpenParen:
ExpressionNode factor = ParseExpression();
ExpectTokenKind(ClosedParen);
return factor;
case Minus:
return new UnaryNode(_currToken, Factor());
case Identifier:
return new IdentNode(_currToken.Value);
case Integer:
return new ConstantNode<int>(int.Parse(_currToken.Value));
case Real:
return new ConstantNode<double>(double.Parse(_currToken.Value));
default:
throw new Exception($"Unexpected {_currToken.Kind} token");
}
}
}
Lexer _lexer; // The lexer that generates a stream of tokens from the source text
Token _currToken; // Current token
}
class Run {
static void Main(string[] args) {
Parser parser = new Parser(File.ReadAllText(args[0]));
ASTNode ast = parser.Parse();
}
}
}