Execute the function on the entire character string

Pages: 12
Hello I have a function to detect comma numbers in a string and convert them to a double variable but I can't make it recursive for all the numbers present? how can I do it?

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
#include <iostream>
#include <string>

double parseFloat(const std::string &input)
{
	const char *p = input.c_str();
	if (! *p || *p == '?')
		return 0;
	int s = 1;
	while (*p == ' ') p++;

	if (*p == '-')
	{
		s = -1;
		p++;
	}

	double acc = 0;
	while (*p >= '0' && *p <= '9')
		acc = acc *10 + *p++ - '0';

	if (*p == '.')
	{
		double k = 0.1;
		p++;
		while (*p >= '0' && *p <= '9')
		{
			acc += (*p++ - '0') *k;
			k *= 0.1;
		}
	}
	if (*p) std::cout << "Invalid numeric format \n";
	return s * acc;
}
int main()
{
	std::string test("228.25 355.7 hello 668.74");
	std::cout << parseFloat(test) << "\n";
	return 0;
}


I want the function to give me the numbers 228.25 , 355.7 and 668.74.
Last edited on
As the function is to return multiple numbers why not have parseFloat() return a vector of double - then within parseFloat you can parse the whole of the input string within a loop?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <iostream>
#include <sstream>
#include <string>
#include <vector>

std::vector<double> parseDouble( const std::string &input )
{
   std::stringstream in( input );
   std::vector<double> result;
   for ( std::string word; in >> word; )
   {
       std::stringstream ss( word );
       double f;
       std::string leftover;
       if ( ss >> f && !( ss >> leftover ) ) result.push_back( f );
   }
   return result;
}

int main()
{
   std::string test("228.25 355.7 hello 668.74");
   for ( double x : parseDouble( test ) ) std::cout << x << '\n';
}
Last edited on
Thank you very much, I want to integrate it into a lexer, but I'll try the problem is that I have to modify a class and I'm not sure I can do it, it's the beginning of an interpreter:

https://userpages.umbc.edu/~park/cs341.f18/projects/proj2.shtml

The code I want to modify does not only take into account the integers, an extract of the code in question :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// Basic Token constructor. Just assigns values.
//
Token::Token(TokenKind kind, int val, string txt) {
   m_kind = kind ;
   m_value = val ;
   m_text = txt ;
}


// Basic SymTabEntry constructor. Just assigns values.
//
SymTabEntry::SymTabEntry(TokenKind kind, int val, operation_t fptr) {
   m_kind = kind ;
   m_value = val ;
   m_dothis = fptr ;
}


and the end of the strings is defined by a dot in the code which makes my challenge even more difficult.

1
2
3
4
5
6
7
8
9
10
11
12
    // is it a string literal? 
         //
         if (line[pos] == '.' && line[pos+1] == '"') {

            pos += 2 ;  // 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++ ;
            }


I will try.
Last edited on
If you're doing a parser/interpreter, the first thing you want is a formal syntax diagram/grammar etc. Preferably this would be of type LL(1) - then its fairly easy to implement as hand-coded.

Then you would do a lexical parser to parse into tokens, then a syntax analyser to analyser the tokens, checking for errors, determining the 'type' of each token (variable, operator, number etc). The result would be a container (often a tree) representing the program/statement which can then be compiled as needed or evaluated. Often the lexical/syntax parts are merged into one unit so that only one pass is needed.

In this context, I'm not sure where your original function requirement is needed?

Given the example

2 3 +

you'd end up with a numeric token with value 2, a numeric token with value 3 and an operator token with value + You wouldn't want a vector of 2 3 returned???
Last edited on
Hello, the problem is when I want to run this line :
2.53 3.88 +
(for example)
That's why I'm looking for a solution but I can't add the double type to the interpreter stack.
That's why I'm trying to modify this code.

It is necessary to retrieve the variable of double type in the program written with the function proposed by Lastchance and add it to the parameter with this line
params.push(tk) ;

I tried the compiler and it tells me that the stack doesn't support DOUBLE.
Last edited on
Make sure that it's a stack<double> and not a stack<float>.
What's the type of params?
OK I see, params is a container of type Token which is:

1
2
3
4
5
6
7
8
9
10
11
12
enum TokenKind { UNKNOWN, KEYWORD, INTEGER, VARIABLE, STRING } ;

   class Token {

   public:

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

   } ;


OK. So you have to produce a valid Token and then push that. If you want to be able to use double, then you'll need to extend TokenKind, have an extra variable of type double and change the constructor to allow a double as well.
Last edited on
Here is all the code as I modified it but my compiler will display this error message :
In member function 'void Sally::mainLoop():
[Error] no matching function for call to 'std::stack<Token>::push(double&)

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
// 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 <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.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 <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);
};

#
endif

Last edited on
and the end of the strings is defined by a dot in the code


No. A string is defined as starting with ." and ending with "

. means print the value of the top of the stack (and remove it?)

Last edited on
According to your error message, your stack is a stack<Token>, not a stack<double>.

When you add a Token to your stack, what values should be used for your Token constructor?
Token(TokenKind kind, int val, string txt)
Last edited on
compiler will display this error message :
In member function 'void Sally::mainLoop():
[Error] no matching function for call to 'std::stack<Token>::push(double&)


See my previous post. The error is that you're trying to push a type double when you have to push a type Token.
I didn't look too closely into the project website's code links, but I don't understand where all this confusion of doubles came from.
Your project website doesn't even mention having to handle doubles.
It says:
- If the token is an integer, push the token on the parameter stack.
- If the token is a string literal, push the token on the stack.
- Otherwise, look for the token in the symbol table.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
 if (tk.m_kind == DOUBLE)
		 {
		 std::stringstream in( it->first );	 
   
   for ( std::string word; in >> word; )
   {
       std::stringstream ss( word );
       double f;
       std::string leftover;
       if ( ss >> f && !( ss >> leftover ) ) params.push(f) ;
   }
	
		  }
		  
		 else if (tk.m_kind == STRING) {

      
            params.push(tk) ;

         }


For the print I've modified it, and the stack<token> I don't know how to modify it so that it accepts the double type?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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) ;
}


and for dot

1
2
3
4
5
6
7
8
9
10
  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++ ;
            }
Last edited on
params.push(f)
Stop pushing a double onto a stack that expects a Token. Push the token on to the stack (your tk variable, presumably).
Last edited on
it->first is the data retrieved from the program and it->second is the type of parameter keyword, text or number? if this is it, the stream to convert to double type should work?
I don't understand or I get my number back then if I don't put it in params? In *Sptr ?
Last edited on
Your project website doesn't even mention having to handle doubles.


Absolutely! The assignment says specifically (my bold):

these are the operations that you have to add to the Sally FORTH interpreter:

Stack operations: DUP DROP SWAP ROT
Variable operations: SET @ !
Comparison operations: < <= == != >= >
Logic operations: AND OR NOT
If statements: IFTHEN ELSE ENDIF
Loop construct: DO UNTIL


everything else is provided - but can be changed if wanted (apart from the type of the containers). So you don't need to do parsing etc as these functions are already provided. Just add the extra code for the new operations.

The code provided on the site above is for a working minimal sally implementation. You just need to add to the provided code to implement these additional operations - no need to do any new parsing.
Last edited on
Pages: 12