Execute the function on the entire character string

Pages: 12
PS Are you actually doing the assignment as asked for in the provided site link - or are you doing your own changes to this for your own learning?

If for your own, then you'll need to define your new syntax and make sure it is consistent with the existing language, post it here and then we'll know what you're trying to achieve.
Knowing that the dot is used to display the line on the screen try to do these operations:

1,3 2 + . CR

1,3 is not an integer, so if what you say is actually true when executing the code you should have 3,3 displayed on the screen, but this will not be the case.

I tried, because I need to be able to perform operations with comma numbers, that's why I want to change the code.

As a reminder, the original code is here:

https://userpages.umbc.edu/~park/cs341.f18/projects/proj2.shtml
Last edited on
This should be OK:

3 2 + . CR

which pushes 3, pushes 2, adds the top 2 numbers on the stack, removes them and replaces with 5, then display and remove the top stack element then display newline?

What is a comma number? What is 1,3 supposed to represent?

Are you trying to do the exercise as described, or adding your own enhancements?
Last edited on
If you mean that 1,3 should be a real number 1.3, then you need to change in sally.cpp

1
2
3
4
5
6
7
8
9
// Try to convert to a number
//
n = strtol(literal.c_str(), &endPtr, 10) ;

if (*endPtr == '\0') {
    tkBuffer.push_back( Token(INTEGER,n,literal) ) ;
} else {
    tkBuffer.push_back( Token(UNKNOWN,0,literal) ) ;
}


to replace strtol() with your own conversion function (and changes to class Token et al), and the buffer push_back statements as needed. Are you going to have only type double for numbers - or do you want to allow both integer and double? If just double, then the arithmetic functions just need to be changed to use double. If you want to allow both, then you'll need to expand to test for the operand types and push the correct Token.

Last edited on
Great! I'm just doing this for me, without worrying about the instructions on the website. It's to learn and advance personally but I couldn't detect where to make the change in the code. This led me to make the previous mistakes.
So it's the strtol() function that I have to replace by the Lastchance function but I have to complete it so that it can handle both types of numbers. I will work on it thanks for your precisions and your precious help.
Actually, it may be easier than we think.

Consider something like this (not tried):

1
2
3
4
5
6
7
8
9
10
11
12
13
n = strtol(literal.c_str(), &endPtr, 10) ;

if (*endPtr == '\0') {
    tkBuffer.push_back( Token(INTEGER,n,literal) ) ;
} else {
    double d = strtod(literal.c_str(), &endPtr);

    if (*endPtr == '\0') {
        tkBuffer.push_back( Token(DOUBLE, d, literal);
    } else {
        tkBuffer.push_back( Token(UNKNOWN,0,literal) ) ;
    }
}


Assuming struct Token has been enhanced to support DOUBLE. This allows separate token types of INTEGER and DOUBLE. However, if you just want DOUBLE instead of INTEGER, then try this and see what happens:

1
2
3
4
5
6
7
n = strtod(literal.c_str(), &endPtr) ;

if (*endPtr == '\0') {
    tkBuffer.push_back( Token(DOUBLE,n,literal) ) ;
} else {
    tkBuffer.push_back( Token(UNKNOWN,0,literal) ) ;
}


where the definition of n has been changed from long int to double (at the top of the function) - and Token has been changed.

Note that in any case, you'll have to change the doMinus(), doTimes() et al functions as they define answer to be of type int.

Also note that the code provided is for a basic simplistic parser using c-like code 'tacked onto' C++ and looks about 20 years old! Don't take this as an example of how to write good, modern C++.
Last edited on
Hello, yes that's what I did except that I changed everything to double type to avoid complications beyond my level in C++. Thanks for your help, it works.
Last edited on
main.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
// File: driver3.cpp
//
// CMSC 341 Fall 2018 Project 2
//
// Simple driver program to call the Sally Forth interpreter
// This version accepts user input for filename of Sally Forth
// source code via command line arguments
//
#include <math.h>
#include <string>
#include <ctime>
#include <iomanip>
#include <iostream>
#include <fstream>
using namespace std;
#include "Sally.h"

int main(int argc, char *args[])
{
	if (argc != 2)
	{
		std::cout << "Invalid number of arguments provided. Expected 2, got " << argc << std::endl;
		std::cout << "This program expects a single argument of<filename>" << std::endl;
	}
	ifstream ifile(args[1]);

	Sally S(ifile);

	S.mainLoop();

	ifile.close();
	return 0;
}

Sally.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
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
// File: Sally.cpp
//
// CMSC 341 Fall 2018 Project 2
//
// Implementation of member functions of Sally Forth interpreter
//
#include <math.h>
#include <string>
#include <ctime>
#include <iomanip>
#include <iostream>
#include <string>
#include <list>
#include <stack>
#include <stdexcept>
#include <cstdlib>
#include <vector>
#include <sstream>
using namespace std;
#include "Sally.h"

Token::Token(TokenKind kind, double val, string txt)
{
	m_kind = kind;
	m_value = val;
	m_text = txt;
}

SymTabEntry::SymTabEntry(TokenKind kind, double val, operation_t fptr)
{
	m_kind = kind;
	m_value = val;
	m_dothis = fptr;
}

Sally::Sally(istream & input_stream):
	istrm(input_stream)	// use member initializer to bind reference
{

	symtab["DUMP"] = SymTabEntry(KEYWORD, 0, &doDUMP);
	symtab["+"] = SymTabEntry(KEYWORD, 0, &doPlus);
	symtab["-"] = SymTabEntry(KEYWORD, 0, &doMinus);
	symtab["*"] = SymTabEntry(KEYWORD, 0, &doTimes);
	symtab["/"] = SymTabEntry(KEYWORD, 0, &doDivide);
	symtab["%"] = SymTabEntry(KEYWORD, 0, &doMod);
	symtab["NEG"] = SymTabEntry(KEYWORD, 0, &doNEG);

	symtab["PRINT"] = SymTabEntry(KEYWORD, 0, &doDot);
	symtab["SP"] = SymTabEntry(KEYWORD, 0, &doSP);
	symtab["CR"] = SymTabEntry(KEYWORD, 0, &doCR);
	symtab["TB"] = SymTabEntry(KEYWORD, 0, &doTB);

}
bool Sally::fillBuffer()
{
	string line;	// single line of input
	int pos;	// current position in the line
	int len;	// # of char in current token
	double n;	// int value of token
	char *endPtr;	// used with strtol()

	while (true)
	{
		// keep reading until empty line read or eof

		// get one line from standard in
		//
		getline(istrm, line);

		// if "normal" empty line encountered, return to mainLoop
		//
		if (line.empty() && !istrm.eof())
		{
			return true;
		}

		// if eof encountered, return to mainLoop, but say no more
		// input available
		//
		if (istrm.eof())
		{
			return false;
		}

		// Process line read

		pos = 0;	// start from the beginning

		// skip over initial spaces &tabs
		//
		while (line[pos] != '\0' && (line[pos] == ' ' || line[pos] == '\t'))
		{
			pos++;
		}

		// Keep going until end of line
		//
		while (line[pos] != '\0')
		{

			// is it a comment?? skip rest of line.
			//
			if (line[pos] == '/' && line[pos + 1] == '/') break;

			// is it a string literal? 
			//
			if (line[pos + 1] == '"')
			{

				pos += 1;	// skip over the ."
				len = 0;	// track length of literal

				// look for matching quote or end of line
				//
				while (line[pos + len] != '\0' && line[pos + len] != '"')
				{
					len++;
				}

				// make new string with characters from
				// line[pos] to line[pos+len-1]
				string literal(line, pos, len);	// copy from pos for len chars

				// Add to token list
				//
				tkBuffer.push_back(Token(STRING, 0, literal));

				// Different update if end reached or " found
				//
				if (line[pos + len] == '\0')
				{
					pos = pos + len;
				}
				else
				{
					pos = pos + len + 1;
				}
			}
			else
			{
				// otherwise "normal" token

				len = 0;	// track length of token

				// line[pos] should be an non-white space character
				// look for end of line or space or tab
				//
				while (line[pos + len] != '\0' && line[pos + len] != ' ' && line[pos + len] != '\t')
				{
					len++;
				}

				string literal(line, pos, len);	// copy form pos for len chars
				pos = pos + len;
				// Try to convert to a number
				//
				//n = strtol(literal.c_str(), &endPtr, 10) ;
				n = strtod(literal.c_str(), &endPtr);

				if (*endPtr == '\0')
				{
					tkBuffer.push_back(Token(DOUBLE, n, literal));
				}
				else
				{
					tkBuffer.push_back(Token(UNKNOWN, 0, literal));
				}
			}

			// skip over trailing spaces &tabs
			//
			while (line[pos] != '\0' && (line[pos] == ' ' || line[pos] == '\t'))
			{
				pos++;
			}
		}
	}
}

// Return next token from tkBuffer.
// Call fillBuffer() if needed.
// Checks for end-of-file and throws exception 
//
Token Sally::nextToken()
{
	Token tk;
	bool more = true;

	while (more && tkBuffer.empty())
	{
		more = fillBuffer();
	}

	if (!more && tkBuffer.empty())
	{
		throw EOProgram("End of Program");
	}

	tk = tkBuffer.front();
	tkBuffer.pop_front();
	return tk;
}

// The main interpreter loop of the Sally Forth interpreter.
// It gets a token and either push the token onto the parameter
// stack or looks for it in the symbol table.
//
//
void Sally::mainLoop()
{

	Token tk;
	map<string, SymTabEntry>::iterator it;

	try
	{
		while (1)
		{
			tk = nextToken();

			if (tk.m_kind == DOUBLE || tk.m_kind == STRING)
			{

				// if DOUBLE or STRING just push onto stack
				params.push(tk);
			}
			else
			{
				it = symtab.find(tk.m_text);

				if (it == symtab.end())
				{
					// not in symtab

					params.push(tk);
				}
				else if (it->second.m_kind == KEYWORD)
				{

					// invoke the function for this operation
					//
					it->second.m_dothis(this);
				}
				else if (it->second.m_kind == VARIABLE)
				{

					// variables are pushed as tokens
					//
					tk.m_kind = VARIABLE;
					params.push(tk);
				}
				else
				{

					// default action
					//
					params.push(tk);
				}
			}
		}
	}
	catch (EOProgram & e)
	{

		cerr << "\t End of Program\n";
		if (params.size() == 0)
		{
			cerr << "\t Parameter stack empty.\n";
		}
		else
		{
			cerr << "Parameter stack has " << params.size() << " token(s).\n";
		}
	}
	catch (out_of_range & e)
	{

		cerr << "\t Parameter stack underflow??\n";
	}
	catch (...)
	{

		cerr << "\t Unexpected exception caught\n";
	}
}
void Sally::doPlus(Sally *Sptr)
{
	Token p1, p2;

	if (Sptr->params.size() < 2)
	{
		throw out_of_range("Need two parameters for +.");
	}
	p1 = Sptr->params.top();
	Sptr->params.pop();
	p2 = Sptr->params.top();
	Sptr->params.pop();
	double answer = p2.m_value + p1.m_value;
	Sptr->params.push(Token(DOUBLE, answer, ""));
}

void Sally::doMinus(Sally *Sptr)
{
	Token p1, p2;

	if (Sptr->params.size() < 2)
	{
		throw out_of_range("Need two parameters for -.");
	}
	p1 = Sptr->params.top();
	Sptr->params.pop();
	p2 = Sptr->params.top();
	Sptr->params.pop();
	double answer = p2.m_value - p1.m_value;
	Sptr->params.push(Token(DOUBLE, answer, ""));
}
void Sally::doTimes(Sally *Sptr)
{
	Token p1, p2;

	if (Sptr->params.size() < 2)
	{
		throw out_of_range("Need two parameters for *.");
	}
	p1 = Sptr->params.top();
	Sptr->params.pop();
	p2 = Sptr->params.top();
	Sptr->params.pop();
	double answer = p2.m_value *p1.m_value;
	Sptr->params.push(Token(DOUBLE, answer, ""));
}
void Sally::doDivide(Sally *Sptr)
{
	Token p1, p2;

	if (Sptr->params.size() < 2)
	{
		throw out_of_range("Need two parameters for /.");
	}
	p1 = Sptr->params.top();
	Sptr->params.pop();
	p2 = Sptr->params.top();
	Sptr->params.pop();
	double answer = p2.m_value / p1.m_value;
	Sptr->params.push(Token(DOUBLE, answer, ""));
}

void Sally::doMod(Sally *Sptr)
{
	Token p1, p2;

	if (Sptr->params.size() < 2)
	{
		throw out_of_range("Need two parameters for %.");
	}
	p1 = Sptr->params.top();
	Sptr->params.pop();
	p2 = Sptr->params.top();
	Sptr->params.pop();
	double answer = (p1.m_value - p2.m_value *(p1.m_value / p2.m_value));
	Sptr->params.push(Token(DOUBLE, answer, ""));
}
void Sally::doNEG(Sally *Sptr)
{
	Token p;

	if (Sptr->params.size() < 1)
	{
		throw out_of_range("Need one parameter for NEG.");
	}
	p = Sptr->params.top();
	Sptr->params.pop();
	Sptr->params.push(Token(DOUBLE, -p.m_value, ""));
}
void Sally::doDot(Sally *Sptr)
{

	Token p;
	if (Sptr->params.size() < 1)
	{
		throw out_of_range("Need one parameter for PRINT");
	}

	p = Sptr->params.top();
	Sptr->params.pop();

	if (p.m_kind == DOUBLE)
	{
		cout << p.m_value;
	}
	else
	{
		cout << p.m_text;
	}
}
void Sally::doTB(Sally *Sptr)
{
	cout << "\t";
}
void Sally::doSP(Sally *Sptr)
{
	cout << " ";
}
void Sally::doCR(Sally *Sptr)
{
	cout << endl;
}
void Sally::doDUMP(Sally *Sptr)
{
	// do whatever for debugging
}
Last edited on
Sally.h

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
// File: Sally.h
//
// CMSC 341 Fall 2018 Project 2
//
// Class declarations for Sally Forth interpreter
//

#ifndef _SALLY_H_
#define _SALLY_H_
#include <math.h>
#include <iostream>
#include <string>
#include <list>
#include <stack>
#include <map>
#include <stdexcept>
using namespace std;

// thrown by lexical parser when end of file reached
//
class EOProgram: public runtime_error
{
	public: EOProgram(const string &what): runtime_error(what) {}
};

enum TokenKind
{
	UNKNOWN, KEYWORD, DOUBLE, VARIABLE, STRING
};

// lexical parser returns a token 
// programs are lists of tokens
//
class Token
{

	public:

		Token(TokenKind kind = UNKNOWN, double val = 0.0, string txt = "");
	TokenKind m_kind;
	double m_value;	// if it's a known numeric value
	string m_text;	// original text that created this token

};

// type of a C++ function that does the work 
// of a Sally Forth operation.
//
class Sally;
typedef void(*operation_t)(Sally *Sptr);

// entry in the symbol table has this type
//
class SymTabEntry
{
	public:
		SymTabEntry(TokenKind kind = UNKNOWN, double val = 0.0, operation_t fptr = NULL);
	TokenKind m_kind;
	double m_value;	// variables' values are stored here
	operation_t m_dothis;	// pointer to a function that does the work
};

// Main Sally Forth class
//
class Sally
{

	public:

		Sally(istream &input_stream = cin);	// make a Sally Forth interpreter

	void mainLoop();	// do the main interpreter loop

	private:

		// Where to read the input
		//
		istream &istrm;

	// Sally Forth operations to be interpreted
	//
	list<Token> tkBuffer;

	// Sally Forth parameter stack
	//
	stack<Token> params;

	// Sally Forth symbol table
	// keywords and variables are store here
	//
	map<string, SymTabEntry> symtab;

	// add tokens from input to tkBuffer
	//
	bool fillBuffer();

	// give me one more token.
	// calls fillBuffer() for you if needed.
	//
	Token nextToken();

	// static member functions that do what has
	// to be done for each Sally Forth operation
	// 
	// pointers to these functions are stored 
	// in the symbol table
	//
	static void doDUMP(Sally *Sptr);	// for debugging
	static void doPlus(Sally *Sptr);
	static void doMinus(Sally *Sptr);
	static void doTimes(Sally *Sptr);
	static void doDivide(Sally *Sptr);
	static void doMod(Sally *Sptr);
	static void doNEG(Sally *Sptr);

	static void doDot(Sally *Sptr);
	static void doSP(Sally *Sptr);
	static void doCR(Sally *Sptr);
	static void doTB(Sally *Sptr);

};

#endif 
Last edited on
I thank this forum without which I would have spent more time to understand what had to be modified in the code to make it work the way I want it to and of course I thank the source site of the original code without which it was not very credible that I started to create my programming language. Thanks to all of you.


364.6046 10 * PRINT CR

;)
Last edited on
Topic archived. No new replies allowed.
Pages: 12