I've been racking my brain trying to figure out the logic with implementing my symbol table & now I'm waving the white flag and asking for some help. I'm creating a symbol table using vectors but I'm having trouble putting the symbol table entry (a string for now, a struct later) in the right scope. My code has an int scopenum that increments every time a block is opened { and decrements every time a block is closed }. However this line causes a problem:
{ a { b } { q } } because it puts q in scope 2 when it should be in scope 3. My code pushes a new row onto the vector but it doesn't use it in the insert. How can I modify my code to correctly account for opening & closing scopes?
#include <iostream>
#include <fstream>
#include <vector>
using namespace std;
int stacknum=-1;
vector< vector<string> > v;
void display(){
for(int i=0; i<v.size(); i++){
cout << "Scope " << i+1 << ": " << endl;
for(int j=0; j<v[i].size(); j++)
cout << v[i][j] << endl;
cout << endl;
}
}
void insert(string s){
v[stacknum].push_back(s);
}
int main(){
string data;
ifstream file;
file.open("input");
if(!file.is_open()) {
cout << "Input file not found";
return 1; }
while(file >> data){
if(data=="{"){
stacknum++;
vector<string> row;
v.push_back(row);
continue;
}
if(data=="}"){
stacknum--;
continue;
}
insert(data);
}
file.close();
display();
return 0;
}
You can't track the current scope with just a index; you need a stack. Alternatively (and probably more usefully) you need to record the parent scope for each scope, which you can do with a second vector or by including an extra data member in the scope object.
In either case, when you enter a new scope, you create a new scope object (a vector of symbols, with associated parent scope index) at the end of the vector of scope objects, and make that object's index the "current scope index". When you leave a scope, the current scope index is set to the current scope's parent.
The parent pointers are useful when you are searching for a symbol; you need to search all of the parents of the current scope until you find the symbol.
Depending on the scoping rules you are trying to model, that might not be adequate. It doesn't accurately model C's scoping rules, for example, because in C, the scope of a declared variable starts at the declaration, not at the beginning of the enclosing block. However it may be good enough for building an AST, as long as you do all symbol lookups in order as you parse the input left to right.
Related
so i get how sentinel loops work, they use the expression at the beginning of the the loop declaration to determine where the loop stops. However i don't understand how to connect variables in the scope of the loop and outside the scope. For example
int i;
int sum = 0;
cout << "Enter a number" << endl;
cin>> i;
while (i > 0)
{
sum = sum + i;
cout << "Enter a number" << endl;
cin>> i;
}
cout << "The sum of the numbers entered is" << endl;
cout << sum;
return 0;
So the sum in the loop is correct since its within scope but the i or number entered to define i out of the scope in the initial i . How do you go about this, can you connect the variables inside and outside the loop? Or can you use uninitialized memory to make a sentinel loop? Do you have to add a separate variable outside the loop and connect the sum of i outside and inside the loop? So just trying to understand how you'd connect the variables inside and outside the scope. If sentinels need to have the variable defined prior to the loop.
Thank you for any light you can shed on this.
Variables are defined in a particular scope and become visible to all scopes within the outer scope unless overwritten (which most programmers tell you is a very, very bad idea). For instance:
#include
using std::cout;
using std::endl;
int main(int, char **) {
int i = 5;
int k = 10;
if (true) {
int j = 15;
int i = 20;
cout << "i inside if: " << i << ". J: " << j << endl;
}
cout << "i outside if: " << i << ". K: " << k << endl;
}
Running it produces:
i inside if: 20. J: 15
i outside if: 5. K: 10
In this case, the scope of method main() contains two variables, i and k. Those variables are also available inside the if-statement. The if also defines two variables, j and i. They are only available inside the if-statement.
The i inside the if-statement hides the larger i (which is dangerous / confusing), but I wanted to demonstrate it. As you can see, i outside the if retains it's original value -- it wasn't modified when we declared the inner copy.
In your code, i and sum are defined outside the if-statement, so they're available inside it, and changes you make to them persist. That is, because you haven't defined new variables, everything just works.
I was studying the STL and decided to write some code to practice the writing and reading of files. The problem consists of creating a list of int (0, 1,...,9), save it in a binary file, and finally load it again.
There are 5 basic code blocks:
1. create list
2. present list
3. save list
4. load list
5. present list again
It seems simple and straightforward; however, the code seems to get in an infinite loop.
int main(){
list<int> numbers;
/////// Create list of 10 integers ///////
for(int i=0; i<10; i++){
numbers.push_back(i);
}
/////// Present List ///////
cout << "List created: [";
list<int>::iterator it;
for(it = numbers.begin(); it != numbers.end(); it++){
if(*it != 9){
cout << *it << ", ";
}
else{
cout << *it;
}
}
cout << "]" << endl;
/////// Save list ///////
string fileName = "test.bin";
ofstream outputFile;
outputFile.open(fileName, ios::binary);
if(outputFile.is_open()){
outputFile.write(reinterpret_cast<char *>(&numbers), sizeof(numbers));
outputFile.close();
cout << "List saved to file." << endl;
}
else{
cout << "Could not open file named " << fileName << endl;
}
/////// Load list ///////
list<int> anotherList;
ifstream inputFile;
inputFile.open(fileName, ios::binary);
if(inputFile.is_open()){
inputFile.read(reinterpret_cast<char *>(&anotherList), sizeof(anotherList));
inputFile.close();
cout << "List loaded from file." << endl;
}
else{
cout << "Could not open file named " << fileName << endl;
}
/////// Present List ///////
cout << "List loaded: [";
for(it = anotherList.begin(); it != anotherList.end(); it++){
if(*it != 9){
cout << *it << ", ";
}
else{
cout << *it;
}
}
cout << "]" << endl;
return 0;
}
The problem is in the "Load List" code block, since, if I comment it out, everything works fine.
Am I saving the object correctly? What am I doing wrong?
Thanks in advance.
The problem lies in the flawed logic of reinterpret_cast<char *>(&numbers). Why?
std::list manages its storage using pointers. It simply holds a pointer to a chain of elements consisting of some objects and a pointer to the next element. You cannot simply treat it like a sequence of bytes and expect it to maintain its functionality.
What you instead need to do is to loop over the elements and write them to the file one by one:
#include <fstream>
#include <iostream>
#include <list>
int main() {
std::fstream file{};
file.open("data.txt", std::ios::binary | std::ios::out);
std::list<int> ints{2, 4, 5, 6, 8, 1, 3, 5, 7, 9};
for (int i : ints) {
file.write(reinterpret_cast<char*>(&i), sizeof(i));
}
file.flush();
file.close();
file.open("data.txt", std::ios::binary | std::ios::in);
ints.clear();
std::cout << "Before reading the file, size of the list is: " << ints.size() << '\n';
for (int i; file.read(reinterpret_cast<char*>(&i), sizeof(i)); ints.push_back(i));
for (int i : ints) {
std::cout << i << ' ';
}
}
Clarification of the second for loop:
for (int i; file.read(reinterpret_cast<char*>(&i), sizeof(i)); ints.push_back(i));
We declare a variable i since we need a place where we read the data. This one should be quite clear. We do not need to initialize i, since the condition of the loop will take care of that (although it would probably be a good practice to do it anyway).
The condition part: file.read(reinterpret_cast<char*>(&i), sizeof(i)). This may seem tricky at first, but it really isn't! First of all, we have a method call. We call std::basic_istream::read, specifying the two arguments - first, the memory address where to read the variable, and second, the number of bytes we want to read. The trick is that the read method not only reads and saves the data - it also returns the stream, so essentially after the data processing, we are left with the condition file. But it's not a bool, is it? Correct, it's not a bool (neither an int), but stream objects can be implicitly converted to bool, which is exactly what happens here! The rules are as follows: if the stream is in a correct state, the conversion returns true. It returns false otherwise. An incorrect state may be cauased, for example, by failure to read, which happens, for example, when you already have read the whole file. Essentially, this part both reads from the file and checks whether the reading process executed successfully. It's both the reading logic and the condition!
The third part: ints.push_back(i). Notice that this part only executes if the condition (reading from the file) executed successfully. It simply adds the read int (i) to the ints container.
All in all, you can read the for loop in the following way:
create a variable i, which will store, one by one, the variables from the file
as long as reading from the file is successful...
...add the read value to the container
outputFile.write(reinterpret_cast<char *>(&numbers), sizeof(numbers));
What you actually print is the binary representation of the list object itself. Unfortunately, it does not contain the data of the list directly, but instead looks similar to something like this:
template <typename T>
class list
{
struct node
{
node* next;
node* previous;
T data;
};
node* m_head;
node* m_tail;
size_t m_size;
public:
// ...
};
No direct link to the data. Even worse: With std::list, the data can get shattered all over your memory (in contrast to std::vector which assures contiguous data).
So you only can iterate over your list again (either with the iterator variant you chose already before or, more convenient, with a range based for loop):
for(auto n : numbers)
{
outputFile.write(reinterpret_cast<char*>(&n), sizeof(n));
}
Reading is different; you don't know the size in advance, do you? Well, there are ways to retrieve it (seekg, tellg), but that's more of interest if you want to read all the data into contiguous memory at once (you could reserve sufficient of in a std::vector), but that's another issue.
For the list approach:
int n;
while(inputFile.read(reinterpret_cast<char*>(&n), sizeof(n)))
{
anotherList.push_back(n);
}
I have this message board, for sale, wanted, type of program below. Ive finally figured out how to read everything in, but need advice on how to compare items. As I read in the file, want to check the array to see if an item that is wanted(true) and mathces the name of an item already in the array and is for sale. If a match is found don't add that item, and delete the wanted item from the array, and shift.
#include <iostream>
#include <sstream>
#include <fstream>
#include <cstdlib>
#include <list>
using namespace std;
struct messageBoard {
string item;
bool forSale;
int price;
};
int main(){
messageBoard board;
const int arrayLength = 100;
std::list<messageBoard> arr;
int index = 0;
string filename;
ifstream words;
cout<<"Please enter the filename:";
cin>>filename;
words.open(filename);
if (words.fail()) {
cout << "file not found or something" << endl;
}else {
string word;;
while (getline(words, word)) {
int wordIndex = 0;
stringstream ss;
ss << word;
while (getline(ss, word, ',')){
if (wordIndex==0){
board.item = word;
}
else if (wordIndex==1&&word==" for sale"){
board.forSale = false;
}
else if (wordIndex==1&&word==" wanted"){
board.forSale = true;
}
else if (wordIndex==2){
board.price = atoi(word.c_str());
}
wordIndex++;
}
index ++;
arr.push_back(board);
}
}
words.close();
for(std::list<messageBoard>::iterator it = arr.begin(); it != arr.end(); it++) {
std::cout << "item: " << (it)->item << " bool: " << (it)->forSale <<"Price: "<<(it)->price << std::endl;
}
}
I won't write the code for you, but I'll tell you how I'd approach the problem.
First, I'd redefine arr as
std::list<MessageBoard> arr;
To add elements to the list, use list::push_back.
if an item that is wanted(true) and mathces the name of an item already in the array
Write a bool function that returns true if those conditions are met, else false.
Scan the list with std::find or std::find_if, using your function. If the search succeeds, the function returns an iterator pointing to the wanted item.
delete the wanted item
Use list::erase on the iterator. If you mess up, the result of deleting an invalid iterator is undefined, but likely your runtime library will let you know, loudly.
and shift
No need. The list length takes care of itself.
You might also consider std::set for efficient searches, or std::map<MessageBoard, int> to keep a count of identical elements.
By using the standard library containers, you elevate your logic from dealing with array elements by location, and move it closer to the problem of matching messages. You'll also write fewer loops.
If I may suggest, I'd call arr something like board or messages. Use the name to convey meaning not known to the compiler. Also, atoi is a bit out of place here. Since you're already in stringstream territory, you might as well extract the price that way.
stringstream sw(word);
sw >> arr[index].price;
HTH.
I have the following program, which is storing four strings in map and printing first time. Now its running the another time to retrieve the stored values. But the second resutls are not same as first time results.
#include <map>
using namespace std;
void fun_call(void **,char * );
main(){
void *data=NULL;
char value[100];
int i=0,j=0;
char key[][10]={"disk1","disk2","disk3","disk4"};
cout << "printing all mapped values " << endl ;
data = (void *) malloc( 100);
for(j=0;j<2;j++){
for(i=0;i<4;i++){
fun_call(&data,key[i]);
memcpy(value,data,100);
cout << "key ="<<key[i]<<" value is " << value << endl;
}
cout <<"====================="<< endl;
}
}
void fun_call(void **tmp,char name[10])
{
void *tmp_data;
char str[100]="ravindra";
int len =0;
static std::map<std::string,void *> name_data_map;
std::map<std::string,void *>::iterator iter ;
iter=name_data_map.find(name) ;
if ( iter == name_data_map.end())
{
len=strlen(str)+strlen(name)+1;
tmp_data = (void *) malloc ( len );
strcat(str,name);
memcpy(tmp_data,str,len);
name_data_map[name]=tmp_data;
cout << "Inside the if" << endl ;
}
else
cout << "disk pos "<< iter->first << endl;
cout << "Outside the if" << endl ;
iter=name_data_map.find(name) ;
memcpy(*tmp,iter->second,len);
}
Output:
$ ./a.out
printing all mapped values
Inside the if
Outside the if
key =disk1 value is ravindradisk1
Inside the if
Outside the if
key =disk2 value is ravindradisk2
Inside the if
Outside the if
key =disk3 value is ravindradisk3
Inside the if
Outside the if
key =disk4 value is ravindradisk4
=====================
disk pos disk1
Outside the if
key =disk1 value is ravindradisk4
disk pos disk2
Outside the if
key =disk2 value is ravindradisk4
disk pos disk3
Outside the if
key =disk3 value is ravindradisk4
disk pos disk4
Outside the if
key =disk4 value is ravindradisk4
any idea why the second iteration is giving all data as : "ravindradisk4"
len is set to 0 in the beginning of fun_call, so if in the second run it doesn't go into your if, memcpy copies 0 bytes in the end. So the last value in main() from the first iteration remains the same regardless of key.
There are a lot of things wrong with your code, if it is intended to be a valid (or remotely idiomatic) C++ program.
As #starbugs points out, you're not using the right length the second time through to copy your result out. The one-line "fix" would be to change:
memcpy(*tmp,iter->second,len);
...to:
memcpy(*tmp,iter->second,strlen((char*)iter->second)+1);
For some basics on why brittle C string techniques are best replaced with C++ methodology, I like to show people this:
Learning Standard C++ As A New Language (PDF) by Bjarne
Once you've grasped that you might be more able to embrace the spirit in which C++ and the standard library should be used.
Your program is so trivial that it's easy to show how it can be simplified to produce idiomatic code which is far more robust and easy to read:
#include <map>
#include <iostream>
#include <string>
using namespace std;
string fun_call(string name)
{
static map<string,string> name_data_map;
map<string,string>::iterator iter;
iter = name_data_map.find(name);
if (iter == name_data_map.end()) {
string mapvalue = "ravindra";
mapvalue += name;
name_data_map[name] = mapvalue;
cout << "Inside the if" << endl ;
}
else
cout << "disk pos "<< iter->first << endl;
cout << "Outside the if" << endl;
iter = name_data_map.find(name) ;
return iter->second;
}
int main() {
string keys[] = {"disk1","disk2","disk3","disk4"};
cout << "printing all mapped values " << endl ;
for(int j = 0; j < 2; j++) {
for(int i = 0; i < 4; i++){
string value = fun_call(keys[i]);
cout << "key =" << keys[i] <<" value is " << value << endl;
}
cout << "=====================" << endl;
}
}
I'll stop there at providing a basically equivalent program with the same output and control flow.
Notes:
In standard C++, main must have an int as the return type (though it doesn't need arguments or a return statement, oddly enough)
The using namespace std; line frees you from having to type std:: in front of things in front of standard library classes like string, map, and their iterators. But don't put that in header files because it can cause problems with other source files that include them and have their own definitions which might conflict with the standard names when not disambiguated.
If you use the standard library then value types do their memory management under the hood, and the memory they use is allocated inside the class and freed in the destructor. Should you ever need to do explicit memory management then use new and delete.
First off, in general, in C++ consider using new/delete instead of malloc()/free().
I am not sure what you are exactly trying to accomplish (i.e. why you continuously copy values) but you have no length set so memcpy() doesn't copy anything.
Another simple fix to this issue is to use the pointer stored in iter->second (note that you would be able to modify data then and update that map entry - so perhaps this is not what you want).
For instance, do not allocate memory for your data variable in main and simply change this line
memcpy(*tmp, iter->second, len);
to
*tmp = iter->second;
Now the pointer address of data in main is set to the pointer address stored in the map.
First of all, I'm not even sure how your code compiles. Your main function lacks a return type and void/no-return is just bad practice. Restructure it to accomodate a simple return of 0 and make its return type int.
Furthermore, several includes are lacking before it even compiles (namely, iostream and string). Instead of using using namespace std, try to "pull" only the things you need from the std namespace. Bringing it all in is a potential hazard and bad practice in general, because you might encounter naming convention collisions in the future (and that will bring forth lots of headaches).
Back to the issue at hand. You're, if you're not experimenting/punishing your mind, applying some very bad practices here. This much memory-copying and pointer shifting around I don't get to do even while I'm working with moving vertex buffers around. And match your allocations with deallocations, that's some very bad memory management. And in C++, we use new/delete.
Since you're passing in the address of the pointer to the data variable, you can simply modify data's pointer by using *tmp.
Since your name_data_map is static, it survives the loop. Therefore, the second data member of the iter is the actual pointer to the data object at hand. Simply change the last line of code of your second function:
*tmp = iter->second;
Anyways, that's my two cents... I don't even get what you're trying to do. Good luck!
#include <iostream>
#include <stack>
#include <string>
using namespace std;
int main()
{
string name;
double gpa;
double high = 0;
stack<string>names;
for (int i=0;i<7;i++)
{
cout << " Enter student's name and gpa " <<endl;
cin >> gpa ;
cin >> name ;
if (gpa > high)
{
names.destroyStack();
high = gpa;
names.push(name);
}
else
if (gpa==high)
{
names.push(name);
}
}
cout << "Highest gpa is " << high << "Names with the highest gpa are"<< endl;
while (!names.empty)
{
cout << names.top() << endl;
names.pop();
}
return 0;
}
In order to display only the names with the highest gpa, I have to put a code to remove the stack before with the lower gpa scores.So for this I thought I could use the "destroystack()" operation but when I use that and try and execute it, the compiler says that the destroyStack wasn't declared in the scope.
This AND the bottom one where I want to display the stack.It even says that empty wasn't declared.
I'm confused with these errors and I don't know what it means by declaring the operations? I'm using codeblocks (Not Visual studio) so does that affect anything?
Because, quite simply, there is no such function destroyStack in std::stack. I have no idea where you got the idea that there is.
This code should work to empty your stack:
replace:
names.destroyStack();
with:
while (!names.empty())
{
names.pop();
}
As you can see, empty is a function; it returns a value. In this case, it returns a boolean (true/false), so you'll need to have parenthesis after it, in order to call it.
That's why you're getting the message about empty not being declared; it means that the compiler is looking for a variable called empty, but it doesn't exist. By adding the parens, you're telling it that you want to call a function, not access a variable.
The "while loop" iterates through all of the items in the stack until the stack is empty. This effectively means that, for every item that the stack has in it, the item is "pop'd" off (pop is also a function, but it returns the item that was on the stack). Eventually, the stack has nothing left in it, and the while loop exits, because empty() returns true.
For a good reference on what functions and properties the stack template has on it, check out:
http://www.cppreference.com/wiki/container/stack/start
You should implement that destroyStack yourself. A function such as:
void destroyStack(stack<string>& _stack)
{
// Do whatever
}
And call it with:
destroyStack(names);
empty should be empty() instead.
Your editor, codeblocks or Visual Studio, doesn't affect anything.