Full code is below. Any feedback is welcomed.
I am trying to implement syntax error reporting
like GCC
and other interpreters/compilers
. I am sure this can be improved and hence why I was pointed to this part of Stackexchange from SO.
Full program:
#include <iostream>
#include <string.h>
#include <string_view>
#include <iomanip>
#include <cstring>
#include <stack>
int main(int argc, char const *argv[])
{
std::string code = "(* n (fac (- n 1))";
int line = 0;
int column = 0;
int position = 0;
int length = code.length();
bool balanced;
std::stack<char> parantheses_stack;
for(const char c: code){
if(c == '\n'){
++line;
column = 0;
} else {
++column;
}
if(c == ')' && parantheses_stack.empty())
balanced = false;
else if(c == '('){
parantheses_stack.push(c);
continue;
}
else if(!parantheses_stack.empty() && c == ')'){
if(parantheses_stack.top() == '('){
parantheses_stack.pop();
}
else if(parantheses_stack.top() != '('){
balanced = false;
}
}
}
balanced = (!parantheses_stack.empty()) ? false : true;
if(!balanced){
printf("Data:\nline: %d\ncolumn: %d\nposition: %d\nlength: %d\n\n", line, column, position, length);
printf("Syntax error:%d, %d parathesis out of balance, expected a ')'\n", line, column + 1);
std::cout << code + "\n";
std::cout << std::setw(column + 2) << "^\n";
}
system("pause");
return 0;
}
Output:
1 Answer 1
You don't handle the case of too many closing parentheses
If there are too many closing parentheses, you set balanced = false
inside the for
-loop, but then continue processing characters. And the value of balanced
is overwritten immediately after the end of the loop. Furthermore, you don't even have an error message for this case.
You don't need a stack
If you just want to match parentheses, and not squared/curly/angle brackets, you don't need to maintain a stack. Instead, just keep a counter for how many unmatched open brackets you have seen so far:
int balance = 0;
for (const char c: code) {
// Count lines and columns
if (c == '\n'){
++line;
column = 0;
continue;
} else {
++column;
}
// Handle opening parentheses
if (c == '(') {
++balance;
continue;
}
// Handle closing parentheses
if (c == ')') {
if (--balance < 0) {
break;
}
}
}
After this loop, balance
should be zero. If it's negative, you know there was a closing parenthesis too many, if it's positive, there were too few.
Don't use system()
system()
is a very expensive function call, since it starts a new shell process, which in turn needs to parse and handle the command "pause"
. Apart from being inefficient (although that doesn't matter much in this case), it is also not portable, as this will only work on Windows.
A better way is to use C++ code to wait for any character:
std::cin.get();
Although even better would be to not pause at all. If you start your program from cmd.exe
, the window should not disappear. If you run it from a code editor or IDE, then check if that has an option to pause before closing the window.
Don't mix printf()
and std::cout
Be consistent and use one way to print text. Since this is C++ code, stick with the C++ way to print text. If you really want to use format strings, then you'll need C++20 to get std::format()
, or C++23 to get the best of both worlds with std::print()
, or use the {fmt} library to get that same functionality also in older versions of C++.
Add a test suite
Make the code that checks the balance of parentheses a function that takes a string as an argument. Then you can call it multiple times. You can then make a test suite that runs it for different input strings that check different corner cases.
-
1\$\begingroup\$ I'd suggest an even more general function which would accept opening and closing characters to check for
{}
or[]
, etc. \$\endgroup\$Edward– Edward2023年12月28日 21:37:47 +00:00Commented Dec 28, 2023 at 21:37 -
\$\begingroup\$ Hi G. Sliepen, what about error syntax reporting? I use Fmt too. \$\endgroup\$Alix Blaine– Alix Blaine2023年12月28日 21:44:43 +00:00Commented Dec 28, 2023 at 21:44