<< operator creation

Hi everyone, I've been searching online for days on how to create a << operator for my assignment. Whatever I found online doesn't seem to work for the assignment I have. I would really appreciate help and guidance on as to where to create the << operator for my program. One of the directions by my professor is "implement operator<< for Value (for use by print)".
I'm posting my ParseNode.h and ParseNode.cpp file.

ParseNode.h File

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
#ifndef PARSENODE_H_
#define PARSENODE_H_

#include <iostream>
#include <string>
#include <vector>
#include <map>
#include <ostream>

using std::istream;
using std::cout;
using std::endl;
using std::string;

#include "polylex.h"

extern int globalErrorCount;

// objects in the language have one of these types
enum Type {
  INTEGERVAL,
  FLOATVAL,
  STRINGVAL,
  UNKNOWNVAL,
};

// this class will be used in the future to hold results of evaluations
class Value {
  int i;
  float f;
  string s;
  Type t;

 public:
  Value(int i) : i(i), f(0), t(INTEGERVAL) {}
  Value(float f) : i(0), f(f), t(FLOATVAL) {}
  Value(string s) : i(0), f(0), s(s), t(STRINGVAL) {}
  Value() : i(0), f(0), s(0) {}

  Type GetType() { return t; }
  int GetIntValue();
  float GetFloatValue();
  string GetStringValue();

  Value operator+(const Value& op) const {
    if (t == INTEGERVAL) {
      // I can add to another integer or float
      if (op.t == INTEGERVAL)
        return Value(i + op.i);
      else if (op.t == FLOATVAL)
        return Value(i + op.f);
    } else if (t == FLOATVAL) {
      // I can add to another integer or float
      if (op.t == INTEGERVAL)
        return Value(f + op.i);
      else if (op.t == FLOATVAL)
        return Value(f + op.f);
    } else if (t == STRINGVAL) {
      if (op.t == STRINGVAL)
        return Value(s + op.s);
    }
    return Value();  // invalid!
  }

  Value operator-(const Value& op) const {
    if (t == INTEGERVAL) {
      // I can add to another integer or float
      if (op.t == INTEGERVAL)
        return Value(i - op.i);
      else if (op.t == FLOATVAL)
        return Value(i - op.f);
    } else if (t == FLOATVAL) {
      // I can add to another integer or float
      if (op.t == INTEGERVAL)
        return Value(f - op.i);
      else if (op.t == FLOATVAL)
        return Value(f - op.f);
    }
    return Value();  // invalid!
  }

  Value operator*(const Value& op) const {
    if (t == INTEGERVAL) {
      // I can add to another integer or float
      if (op.t == INTEGERVAL)
        return Value(i * op.i);
      else if (op.t == FLOATVAL)
        return Value(i * op.f);
    } else if (t == FLOATVAL) {
      // I can add to another integer or float
      if (op.t == INTEGERVAL)
        return Value(f * op.i);
      else if (op.t == FLOATVAL)
        return Value(f * op.f);
    } else if (t == STRINGVAL) {
      if (op.t == INTEGERVAL) {
        return Value(t);
      }
    }
    return Value();  // invalid!
  }
};

// every node in the parse tree is going to be a subclass of this node
class ParseNode {
 protected:
  ParseNode* left;
  ParseNode* right;

 public:
  ParseNode(ParseNode* left = 0, ParseNode* right = 0)
      : left(left), right(right) {}
  virtual ~ParseNode() {}

  virtual Type GetType() { return UNKNOWNVAL; }

  virtual Value Eval(std::map<string, Value>& symb) {
    if (left)
      left->Eval(symb);
    if (right)
      right->Eval(symb);
    return Value();
  }

  virtual void RunStaticChecks(std::map<string, bool>& idMap) {
    if (left)
      left->RunStaticChecks(idMap);
    if (right)
      right->RunStaticChecks(idMap);
  }
};

// a list of statements is represented by a statement to the left, and a list of
// statments to the
// right
class StatementList : public ParseNode {
 public:
  StatementList(ParseNode* l, ParseNode* r) : ParseNode(l, r) {}
};

// a SetStatement represents the idea of setting id to the value of the Expr
// pointed to by the left
// node
class SetStatement : public ParseNode {
  string id;

 public:
  SetStatement(string id, ParseNode* exp) : id(id), ParseNode(exp) {}

  void RunStaticChecks(std::map<string, bool>& idMap) {
    left->RunStaticChecks(idMap);
    idMap[id] = true;
  }
};

// a PrintStatement represents the idea of printing the value of the Expr
// pointed to by the left
// node
class PrintStatement : public ParseNode {
 public:
  PrintStatement(ParseNode* exp) : ParseNode(exp) {}
};

// represents multiplying the two child expressions
class TimesOp : public ParseNode {
 public:
  TimesOp(ParseNode* l, ParseNode* r) : ParseNode(l, r) {}

  Value Eval(std::map<string, Value>& symb) {
    Value op1 = left->Eval(symb);
    Value op2 = right->Eval(symb);
    Value mult = op1 * op2;
    if (mult.GetType() == UNKNOWNVAL) {
      std::cerr << "RUNTIME ERROR: type mismatch in multiplication" << endl;
    }
    /*else
    {
       cout << mult << endl;
    }*/
    return mult;
  }
};

// a representation of a list of coefficients must be developed
class Coefficients : public ParseNode {
 public:
  Coefficients(std::vector<ParseNode*> p) : ParseNode() {}
  // Type GetType() { return std::vector<int >();}
  // Value Eval(std::vector<ParseNode *> p){
  // return p;
  //}
};

// leaves of the parse tree
// notice that the parent constructors take no arguments
// that means this is a leaf
class Iconst : public ParseNode {
  int iValue;

 public:
  Iconst(int iValue) : iValue(iValue), ParseNode() {}
  Type GetType() { return INTEGERVAL; }

  Value Eval(std::map<string, Value>& symb) { return Value(iValue); }
};

class Fconst : public ParseNode {
  float fValue;

 public:
  Fconst(float fValue) : fValue(fValue), ParseNode() {}
  Type GetType() { return FLOATVAL; }

  Value Eval(std::map<string, Value>& symb) { return Value(fValue); }
};

class Sconst : public ParseNode {
  string sValue;

 public:
  Sconst(string sValue) : sValue(sValue), ParseNode() {}
  Type GetType() { return STRINGVAL; }

  Value Eval(std::map<string, Value>& symb) { return Value(sValue); }
};

class Ident : public ParseNode {
  string id;

 public:
  Ident(string id) : id(id), ParseNode() {}
  // Type GetType(); // not known until run time!

  void RunStaticChecks(std::map<string, bool>& idMap) {
    if (idMap[id] == false) {
      // runtimeError("identifier " + id + " used before set");
      std::cerr << "RUNTIME ERROR: identifier " + id + " used before set"
                << endl;
    }
  }

  // Type GetType();
};

// represents adding, the two child expressions
class PlusOp : public ParseNode {
 public:
  PlusOp(ParseNode* l, ParseNode* r) : ParseNode(l, r) {}

  Value Eval(std::map<string, Value>& symb) {
    Value op1 = left->Eval(symb);
    Value op2 = right->Eval(symb);
    Value sum = op1 + op2;
    if (sum.GetType() == UNKNOWNVAL) {
      std::cerr << "RUNTIME ERROR: type mismatch in add" << endl;
    }
    return sum;
  }
};

// represents subtracting, the two child expressions
class MinusOp : public ParseNode {
 public:
  MinusOp(ParseNode* l, ParseNode* r) : ParseNode(l, r) {}

  Value Eval(std::map<string, Value>& symb) {
    Value op1 = left->Eval(symb);
    Value op2 = right->Eval(symb);
    Value sub = op1 - op2;
    if (sub.GetType() == UNKNOWNVAL) {
      std::cerr << "RUNTIME ERROR: type mismatch in subtraction" << endl;
    }
    return sub;
  }
};

extern ParseNode* Prog(istream& in);
extern ParseNode* Stmt(istream& in);
extern ParseNode* Expr(istream& in);
extern ParseNode* Term(istream& in);
extern ParseNode* Primary(istream& in);
extern ParseNode* Poly(istream& in);
extern ParseNode* Coeffs(istream& in);
extern ParseNode* EvalAt(istream& in);

// std::ostream& operator<<( std::ostream& o, ParseNode& ft );

#endif /* PARSENODE_H_ */ 
ParseNode.cpp File

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
#include "ParseNode.h"
#include "polylex.h"
#include <vector>
#include <map>
#include <ostream>

// We want to use our getToken routine unchanged... BUT we want to have the
// ability
// to push back a token if we read one too many; this is our "lookahead"
// so we implement a "wrapper" around getToken

static bool pushedBack = false;
static Token pushedToken;

std::map<string, bool> IdentifierMap;
std::map<string, Value> SymbolTable;

Token GetToken(istream& in) {
  if (pushedBack) {
    pushedBack = false;
    return pushedToken;
  }

  return getToken(in);
}

void PutBackToken(Token& t) {
  if (pushedBack) {
    cout << "You can only push back one token!" << endl;
    exit(0);
  }

  pushedBack = true;
  pushedToken = t;
}

// handy function to print out errors
void error(string s) {
  cout << "PARSE ERROR: " << currentLine << " " << s << endl;
  ++globalErrorCount;
}

// Prog := Stmt | Stmt Prog
ParseNode* Prog(istream& in) {
  ParseNode* stmt = Stmt(in);

  if (stmt != 0)
    return new StatementList(stmt, Prog(in));
  else {
    return 0;
  }
  return 0;
}

// Stmt := Set ID Expr SC | PRINT Expr SC
ParseNode* Stmt(istream& in) {
  Token cmd = GetToken(in);

  if (cmd == SET) {
    Token idTok = GetToken(in);
    if (idTok != ID) {
      error("Identifier required after set");
      return 0;
    }
    ParseNode* exp = Expr(in);
    if (exp == 0) {
      error("expression required after id in set");
      return 0;
    }
    if (GetToken(in) != SC) {
      error("semicolon required");
      return 0;
    }

    return new SetStatement(idTok.getLexeme(), exp);
  } else if (cmd == PRINT) {
    ParseNode* exp = Expr(in);
    if (exp == 0) {
      error("expression required after id in print");
      return 0;
    }
    if (GetToken(in) != SC) {
      error("semicolon required");
      return 0;
    }

    return new PrintStatement(exp);
  } else if (cmd == DONE) {
    return 0;
  } else
    PutBackToken(cmd);
  error("Unrecognized beginning of statement");
  return 0;
}

// Expr := Term { (+|-) Expr }
ParseNode* Expr(istream& in) {
  ParseNode* t1 = Term(in);
  if (t1 == 0) {
    return 0;
  }

  for (;;) {
    Token op = GetToken(in);
    if (op != PLUS && op != MINUS) {
      PutBackToken(op);
      return t1;
    }

    ParseNode* t2 = Expr(in);
    if (t2 == 0) {
      error("expression required after + or - operator");
      return 0;
    }

    if (op == PLUS)
      t1 = new PlusOp(t1, t2);
    else
      t1 = new MinusOp(t1, t2);
  }

  return t1;
}

// Term := Primary { * Term }
ParseNode* Term(istream& in) {
  ParseNode* t3 = Primary(in);
  if (t3 == 0) {
    return 0;
  }

  for (;;) {
    Token op = GetToken(in);
    if (op != STAR) {
      PutBackToken(op);
      return t3;
    }

    ParseNode* t4 = Term(in);
    if (t4 == 0) {
      error("expression required after * operator");
      return 0;
    }

    if (op == STAR)
      t3 = new TimesOp(t3, t4);
  }

  return t3;
}

// Primary :=  ICONST | FCONST | STRING | ( Expr ) | Poly
ParseNode* Primary(istream& in) {
  // check tokens... or call Poly

  Token cmd = GetToken(in);

  if (cmd == ICONST) {
    Token tok2 = GetToken(in);
    if (tok2 != LSQ) {
      PutBackToken(tok2);
      int icmd = std::stoi(cmd.getLexeme());
      return new Iconst(icmd);
    }
  } else if (cmd == FCONST) {
    float fcmd = std::stof(cmd.getLexeme());
    return new Fconst(fcmd);
  } else if (cmd == STRING) {
    return new Sconst(cmd.getLexeme());
  }

  else if (cmd == LPAREN) {
    ParseNode* expr = Expr(in);
    if (expr == 0) {
      error("expected expression");
      return 0;
    }

    if (GetToken(in) != RPAREN) {
      error("Missing )");
      return 0;
    }

    /*else
    {
       return Expr(in);
    }*/
  }

  else {
    PutBackToken(cmd);
    ParseNode* poly = Poly(in);
    if (poly == 0) {
      error("Expecting primary");
      return 0;
    } else {
      return poly;
    }
  }

  return 0;
}

// Poly := LCURLY Coeffs RCURLY { EvalAt } | ID { EvalAt }
ParseNode* Poly(istream& in) {
  // note EvalAt is optional

  Token cmd = GetToken(in);
  if (cmd == LBR) {
    ParseNode* coeff = Coeffs(in);
    if (coeff == 0) {
      error("expected coefficient");
      return 0;
    }

    Token cmd2 = GetToken(in);
    if (cmd2 != RBR) {
      error("Missing }");
      return 0;
    }

    ParseNode* evalAt = EvalAt(in);
    if (evalAt == 0) {
      return coeff;
    } else
      return new StatementList(coeff, evalAt);
  } else if (cmd == ID) {
    ParseNode* evalAt = EvalAt(in);

    if (evalAt == 0) {
      return new Ident(cmd.getLexeme());
    } else {
      return evalAt;
    }
  }

  return 0;
}

// notice we don't need a separate rule for ICONST | FCONST
// this rule checks for a list of length at least one

// Coeff := ICONST | FCONST
ParseNode* Coeff(istream& in) {
  Token cmd = GetToken(in);

  if (cmd == ICONST) {
    int icmd = std::stoi(cmd.getLexeme());
    return new Iconst(icmd);
  } else if (cmd == FCONST) {
    float icmd = std::stof(cmd.getLexeme());
    return new Fconst(icmd);
  } else {
    PutBackToken(cmd);
    // error("");
  }

  return 0;
}

// Coeffs := Coeff { , Coeff }
ParseNode* Coeffs(istream& in) {
  std::vector<ParseNode*> coeffs;
  ParseNode* p = Coeff(in);
  if (p == 0)
    return 0;

  coeffs.push_back(p);

  while (true) {
    Token t = GetToken(in);
    if (t != COMMA) {
      PutBackToken(t);
      break;
    }

    p = Coeff(in);
    if (p == 0) {
      error("Missing coefficient after comma");
      return 0;
    }

    coeffs.push_back(p);
  }

  return new Coefficients(coeffs);
}

// EvalAt := LSQ Expr RSQ
ParseNode* EvalAt(istream& in) {
  Token cmd = GetToken(in);
  if (cmd == LSQ) {
    ParseNode* evalAt = Expr(in);
    if (evalAt == 0) {
      error("Missing expression");
      return 0;
    }

    Token cmd2 = GetToken(in);
    if (cmd2 != RSQ) {
      error("Missing ]");
      return 0;
    }

    return evalAt;
  }

  PutBackToken(cmd);

  return 0;
}
Inside the public section of class Value, something like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
friend ostream & operator << (ostream & os, const Value & val)
{  switch (val.t) 
    {
    case INTEGERVAL:  os << val.i;
                                  break;
    case FLOATVAL:      os << val.f;
                                  break;
    case STRINGVAL:   os << val.s; 
                                 break;
    default:                  os << "???";
    }
    return os;
}


Hi AbstractionAnon,
Thank you so much for that. I had done something similar, but I don't know how to utilize that in the print statement class on line 159. I would really appreciate some help, as this is only my fourth month of c++, without anyone actually teaching me how to code in c++.
I'm not sure why you need a PrintStatement class. A PrintStatement is nothing more than a ParseNode, so why not overload the << operator in the ParseNode class?

// In the ParseNode class declaration:
1
2
3
4
5
6
7
friend ostream & operator << (ostream & os, const PasrseNode & node)
{   if (node.left)
       os << *node.left;
    if (node.right)
     os << *node.right;
    return os;
}



Thank You again AbstractionAnon, but I'm not sure How to use any of this to print my data. It get's stored in PrintStatement class as one argument, but I'm not sure how to print this data.
Assuming you overload the << operator in the ParseNode class as I suggested:

 
  std::cout << node << endl;

Last edited on
When I do that in the public section of the print statement class, I get the message "unknown type name 'cout'".
I corrected my above post to std::cout since you did not have using namespace std;.

I was suggesting you eliminate the PrintStatement class. I see no reason for it.
What's the difference between printing a ParseNode and a PrintStatement since a PrintStatement is a ParseNode?
So in the ParseNode.cpp program, Line 87 is where the print statement is used. The input would be "print 8 + 7" for example, and the parser would parse it, until it returns Expr to Statement, where it would store it in the PrintStatement as an object with one argument. I'm just not sure where I should have the command to print out the values.
By the way, thank you so much for engaging this request for help. I really do appreciate this a lot, and I've already learned a lot from you about implementing the "<<" operator.
Line 87: You create a PrintStatement object, but the only thing you do with it is to return it as a ParseNode object (line 56).

If you overload the << operator in ParseNode as I have twice suggested, line 87 would look like:
87
88
  std::cout << *exp << std::endl;      // Print the expression tree
  return exp;                                      // Return the exp node 


Again, no need for the PrintStatement class. It adds no value.


@AbstractionAnnon So after doing what you said, it is printing every input that is set. For example, the input is:

Set x 10;
Print x;

and the output is x.

How do I get it to print the value stored by x?
Ahh, now I see why you had a PrintStatement class.

In either case, to get the value of x, you need to evaluate the expression tree.

@AbstactionAnon
That's exactly what I don't understand how to do. That's why I would really appreciate some guidance on how to evaluate an expression tree. Does that mean I use my Value Eval method, or create a whole new method that prints out the expression tree? If so, how do I initialize it?
I don't really have a good sense of what your grammar is, so it's hard to make suggestions.

When building parse trees, the usual approach is to reduce as soon as possible. i.e. If you have a ParseNode with a left and right term, evaluating the ParseNode involves operating on the left and right sides resulting in a new ParseNode and deleting the left and right sides since they are no longer needed.

The problem I see with your design is that you have two terms in a ParseNode, but not a clear relationship between the terms. Are the two terms supposed to be added? subtracted?
multiplied, etc.

If you were to express your grammar in Backus-Naur Form (BNF), I think you would see that many reductions require three terms. i.e.
1
2
3
addition  ::= expr '+' expr 
expr      ::= number
            ::= '(' expr ')'


Topic archived. No new replies allowed.