-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathexpressionparser.cpp
331 lines (319 loc) · 12.2 KB
/
expressionparser.cpp
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
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
#include <cctype>
#include <string>
#include <queue>
#include <stack>
#include <iostream>
#include <stdlib.h>
#include "expressionparser.h"
using namespace std;
//Fill Constructur In With Any Important Fields To Be Initialized.
//As of right now, we are not making use of any Private Fields.
ExpressionParser::ExpressionParser(){
}
//Determines Whether The Current Input Character = Valid Operator.
bool ExpressionParser::isOperator(char currentOperator){
return (currentOperator == '&'
|| currentOperator == '|'
|| currentOperator == '~'
|| currentOperator == '='
|| currentOperator == '>');
}
//Output The Logical Associativity For Given Operator.
//Default = 'L' Indicating Left Associativity.
char ExpressionParser::getAssociativity(char currentOperator){
switch(currentOperator){
case '&':
return 'L';
case '|':
return 'L';
case '=':
return 'L';
case '>':
return 'L';
case '~':
return 'R';
default:
return 'L';
}
}
//Return The Precedence For Given Operator.
//Used By Shunting Yard Algorithm To Determine Values To Pop From Operator Stack.
//Default = -1.
int ExpressionParser::getPrecedence(char currentOperator){
switch(currentOperator){
case '&':
return 2;
case '|':
return 1;
case '=':
return 0;
case '>':
return 0;
case '~':
return 4;
default:
return -1;
}
}
/** Returns true if the current operator is able to be
* generalized and false otherwise.
* The AND, OR, and Biconditional are generalizable.
*/
bool ExpressionParser::getGenerality(char currentOperator){
switch(currentOperator){
case '&':
return true;
case '|':
return true;
case '=':
return true;
case '>':
return false;
case '~':
return false;
default:
return false;
}
}
/** Checks if the syntax of the input string is correct
* Returns true if so, and false if incorrect or syntax
* is ambiguous
* Ambiguous syntax is defined as any generalized expression
* that could result in two different expressions given different
* orders of operations on the generalized operations.
*
* Ex:
* A|B|C is not ambiguous because A | (B | C) = (A | B) | C
* A>B>C is ambiguous because A > (B > C) != (A > B) > C
* A|B&C is ambiguous because A | (B & C) != (A | B) & C
Ex Test Cases:
A & ~B is not ambiguous
(A & ~B) > (C | D) is not ambiguous
(A & ~(B|C)) > ~D is not ambiguous
A & ~ & C is ambiguous "Error: Expected statement after operator but found another operator"
*/
bool ExpressionParser::isCorrectSyntax(string input, unsigned int & index) {
if(index >= input.length()){
return true;
}
if(input.size() == 0) {
cout << "Error: Input Cannot Be Empty" << endl;
return false;
}
// firstOP holds the first operator found on a line, which will be compared to all future operators
char* firstOp = NULL;
// general holds the generality of he first operator that is found
bool general = false;
// expectStatement is true when the checker will expect a statement next, denoted by an atomic statement or a '('
// when true, the algorithm can also expect the NOT operator, to handle cases such as (A & ~B)
// expectStatement is false when the checker will be expecting an operator next (excluding the NOT operator)
bool expectStatement = true;
// search through the length of the string
while(index < input.length()) {
// if subexpression found, recursively call this function on it
if(input[index] == '('){
// if the checker was expecting an operator and found a statement, throw an error
if(!expectStatement) {
cout << "Error: Expected operator after statement but found a statement" << endl;
firstOp = NULL;
return false;
}
// if subexpression not correct syntax, return false
index++;
// after finding a statement, expectStatment is changed
expectStatement = false;
if(!isCorrectSyntax(input, index)){
return false;
}
}
// if the current subexpression ends, return true
else if(input[index] == ')'){
// if the checker was expecting a statment and found a ')', throw an error
if(expectStatement) {
cout << "Error: Expected statement after operator but found closed parentheses" << endl;
firstOp = NULL;
return false;
}
index++;
return true;
}
// if the NOT operator is found
else if(input[index] == '~') {
// if the checker was expecting a non-NOT operator and found a NOT, throw an error
if(!expectStatement) {
cout << "Error: Expected non-NOT operator after statement but found a NOT operator" << endl;
firstOp = NULL;
return false;
}
index++;
}
// if an operator is found that is not the NOT operator
else if(isOperator(input[index]) && input[index] != '~'){
// if the checker was expecting a statement and found an operator, throw an error
if(expectStatement) {
cout << "Error: Expected statement after operator but found another operator" << endl;
firstOp = NULL;
return false;
}
// after finding an operator, expectStatment is changed
expectStatement = true;
// if it is the first operator on this level, store it
if(firstOp == NULL) {
firstOp = &input[index];
general = getGenerality(*firstOp);
}
// if it is not the first operator
else {
// if it is a different operator
// or if the first operator is not generalizable
if(input[index] != *firstOp || general == false) {
cout << "Error: Ambiguous Statement With Operators " << *firstOp << " and " << input[index] << endl;
firstOp = NULL;
return false;
}
}
index++;
}
// else if an atomic statement is found
else {
// if the checker was expecting an operator and found an atomic statement, throw an error
if(!expectStatement) {
cout << "Error: Expected operator after statement but found atomic statment" << endl;
firstOp = NULL;
return false;
}
// after finding a statement, expectStatment is changed
expectStatement = false;
index++;
}
}
return true;
}
// Simply Removes ' ' From Logical Expressions. Then calls to check the syntax of the statement.
string ExpressionParser::formatInputValue(string currentInput){
string tempInput = "";
for(char currentChar : currentInput){
if(currentChar != ' '){
tempInput += currentChar;
}
}
// Calls isCorrectSyntax on the entire statement and returns an error if
// the syntax is wrong or ambiguous
unsigned int testIndex = 0;
if(!isCorrectSyntax(tempInput, testIndex)) {
cout << tempInput << endl;
return "ERROR";
}
return tempInput;
// char prevOneValue = ' ';
// char prevTwoValue = ' ';
// string newFormatInput = "";
// for(int k=0; k<currentInput.size(); k++){
// char currentValue = currentInput[k];
// newFormatInput += currentValue;
// if(prevOneValue == '-' && currentValue == '>'){
// newFormatInput.pop_back();
// newFormatInput.pop_back();
// newFormatInput += '/';
// }
// if(prevTwoValue == '<' && prevOneValue == '-' && currentValue == '>'){
// newFormatInput.pop_back();
// newFormatInput.pop_back();
// newFormatInput.pop_back();
// newFormatInput += '%';
// }
// if(!isalpha(currentValue) && !isOperator(currentValue)
// && currentValue != '<' && currentValue != '-' && currentValue != '>'
// && currentValue != '(' && currentValue != ')' && currentValue != ' '){
// cout << currentValue << endl;
// return "";
// }
// prevTwoValue = prevOneValue;
// prevOneValue = currentValue;
// }
// return newFormatInput;
}
//Below is the Implementation of the Well-Known Shunting Yard Algorithm.
//This Algorithm is described more in detail on the Wiki Pages of the ALPACA-LOGIC Branch.
//Simply, = O(n) Expression Parser For A Given String => Reverse Polish Notation.
string ExpressionParser::runShuntingYardAlgorithm(string inputExpression){
//Main Output Queue:
queue<char> outQueue;
//Main Operator Stack:
stack<char> opStack;
//Formats Input String By Removing Any Spaces.
inputExpression = formatInputValue(inputExpression);
if(inputExpression == "ERROR"){
return "ERROR";
}
//Constants For Associativity Values:
char leftValue = 'L';
char rightValue = 'R';
//Constants For Paranthesis Values:
char leftP = '(';
char rightP = ')';
//While There Are More Tokens:
for(char token: inputExpression){
//Case 1: Valid Operator.
if(isOperator(token)){
while(!opStack.empty()
&& isOperator(opStack.top())
&& ((getAssociativity(token) == leftValue && getPrecedence(opStack.top()) >= getPrecedence(token))
|| (getAssociativity(token) == rightValue && getPrecedence(opStack.top()) > getPrecedence(token)))) {
outQueue.push(opStack.top());
opStack.pop();
}
opStack.push(token);
}
//Case 2: Left Paranthesis '('
else if(token == leftP){
opStack.push(token);
}
//Case 3: Right Paranthesis ')'
else if(token == rightP) {
while(!opStack.empty() && opStack.top() != leftP){
outQueue.push(opStack.top());
opStack.pop();
}
//Error Check For Mismatched Paranthesis:
//If Stack = Empty w/o Finding a Left Parenthesis,
//then there are Mismatched Parentheses => Invalid Expression.
//"ERROR" = Communication to Client = Tree.cpp To Construct Tree.
if(opStack.empty()){
cout << "Error: Mismatched Parentheses." << endl;
return "ERROR";
}
if(opStack.top() == '('){
opStack.pop();
}
}
else{
//Error Check For Non-Operator + Non-Alpha Characters:
//(e.g., Numerical Values, ...)
if(!isalpha(token)){
cout << "Error: Invalid Operator." << endl;
return "ERROR";
}
//Append Current Value To Main Output Queue.
outQueue.push(token);
}
}
//Push Any Remaining Operators Onto Output Queue:
while(!opStack.empty()){
//Error Check For Mismatched Paranthesis:
if(opStack.top() == '(' || opStack.top() == ')'){
cout << "Error: Mismatched Parentheses." << endl;
return "ERROR";
}
outQueue.push(opStack.top());
opStack.pop();
}
//Format/Set Output Result String By Obtaining All Values From Output Queue.
string resultValue = "";
while(!outQueue.empty()) {
resultValue += outQueue.front();
outQueue.pop();
}
//Return Reverse Polish Notation of Input Expression.
return resultValue;
}