Skip to content

Commit

Permalink
parser: use standard functions to parse numbers
Browse files Browse the repository at this point in the history
modified the get number function to use standard library functions to
parse number and convert them; the input string is temporarily put into
a standard input string stream and read from it (the entire parser will
be changed to this) and as characters of the number are parsed, they are
moved from the input stream into a local standard string that will be
passed (moved) to the token constructors when the token is created

modified the token constructors for creating integer and double constant
tokens to take a standard string, though for now it is converted to a
c-style string to initialize the QString member variable
  • Loading branch information
thunder422 committed Nov 2, 2014
1 parent 20e46cc commit 8513875
Show file tree
Hide file tree
Showing 4 changed files with 59 additions and 51 deletions.
90 changes: 47 additions & 43 deletions parser.cpp
Expand Up @@ -241,53 +241,55 @@ void Parser::skipWhitespace(void)

TokenUniquePtr Parser::getNumber()
{
// TODO temporary to simulate m_input as input string stream
std::string tmp {m_input.mid(m_pos).toStdString()};
std::istringstream m_input {tmp};

bool digits {}; // digits were found flag
bool decimal {}; // decimal point was found flag
bool sign {}; // have negative sign flag
bool expSign {}; // have exponent sign flag
std::string number; // string to hold number

int pos {m_pos};
forever
{
if (m_input[pos].isDigit())
if (isdigit(m_input.peek()))
{
pos++; // move past digit
number.push_back(m_input.get()); // get digit
if (!digits) // first digit?
{
digits = true;
if (!decimal && m_input[pos - 1] == '0' && m_input[pos] != '.')
if (!decimal && number.back() == '0' && m_input.peek() != '.')
{
// next character not a digit (or '.')?
if (!m_input[pos].isDigit())
{
break; // single zero, exit loop to process string
}
else
if (isdigit(m_input.peek()))
{
// if didn't find a decimal point
// and first character is a zero
// and second character is not a decimal point,
// and second character is a digit
// then this is in invalid number
throw Error {Status::ExpNonZeroDigit, m_pos, 1};
throw Error {Status::ExpNonZeroDigit, pos, 1};
}
break; // single zero, exit loop to process string
}
}
}
else if (m_input[pos] == '.')
else if (m_input.peek() == '.')
{
if (decimal) // was a decimal point already found?
{
if (!digits) // no digits found?
{
throw Error {Status::ExpDigitsOrSngDP, m_pos, 2};
throw Error {Status::ExpDigitsOrSngDP, pos, 2};
}
break; // exit loop to process string
}
decimal = true;
pos++; // move past '.'
number.push_back(m_input.get()); // get '.'
}
else if (m_input[pos].toUpper() == 'E')
else if (toupper(m_input.peek()) == 'E')
{
if (!digits)
{
Expand All @@ -299,23 +301,24 @@ TokenUniquePtr Parser::getNumber()
}
// if there were no digits before 'E' then error
// (only would happen if mantissa contains only '.')
throw Error {Status::ExpManDigits, m_pos, 2};
throw Error {Status::ExpManDigits, pos, 2};
}
pos++; // move past 'e' or 'E'
if (m_input[pos] == '+' || m_input[pos] == '-')
number.push_back(m_input.get()); // get 'e' or 'E'
if (m_input.peek() == '+' || m_input.peek() == '-')
{
expSign = true;
pos++; // move past exponent sign
number.push_back(m_input.get()); // get exponent sign
}
// now look for exponent digits
digits = false;
while (m_input[pos].isDigit())
while (isdigit(m_input.peek()))
{
pos++; // move past exponent digit
number.push_back(m_input.get()); // get exponent digit
digits = true;
}
if (!digits) // no exponent digits found?
{
pos += number.length(); // move to error
throw Error {expSign
? Status::ExpExpDigits : Status::ExpExpSignOrDigits, pos,
1};
Expand All @@ -328,9 +331,9 @@ TokenUniquePtr Parser::getNumber()
if (!digits && !decimal) // nothing found?
{
// look for negative sign
if (!sign && m_input[pos] == '-')
if (!sign && m_input.peek() == '-')
{
pos++; // move past negative sign
number.push_back(m_input.get()); // get negative sign
sign = true;
}
else
Expand All @@ -340,7 +343,7 @@ TokenUniquePtr Parser::getNumber()
}
else if (!digits) // only a decimal point found?
{
throw Error {Status::ExpDigits, m_pos, 1};
throw Error {Status::ExpDigits, pos, 1};
}
else
{
Expand All @@ -349,37 +352,38 @@ TokenUniquePtr Parser::getNumber()
}
}
}
// pos pointing to first character that is not part of constant
bool ok;
int len {pos - m_pos};
QString numStr {m_input.mid(m_pos, len)};

// FIXME hack for memory issue reported against QString::toInt()/toDouble()
QByteArray numBytes;
numBytes.append(numStr);
int len = number.length();
if (!decimal) // no decimal or exponent?
try
{
// try to convert to integer first
int value {numBytes.toInt(&ok)};
if (ok)
{
std::swap(m_pos, pos); // swap begin and end positions
// save string of number so it later can be reproduced
return TokenUniquePtr{new Token {pos, len, numStr, value}};
}
// else overflow or underflow, won't fit into an integer
// fall thru and try as double
int value {std::stoi(number)};

m_pos += number.length();
// save string of number so it later can be reproduced
return TokenUniquePtr{new Token {pos, len, std::move(number), value}};
}
catch(std::out_of_range)
{
// overflow or underflow, won't fit into an integer
// fall through and try as double
}

try
{
double value {std::stod(number)};

double value {numBytes.toDouble(&ok)};
if (!ok)
m_pos += number.length();
// save string of number so it later can be reproduced
return TokenUniquePtr{new Token {pos, len, std::move(number), value,
decimal}};
}
catch (std::out_of_range)
{
// overflow or underflow, constant is not valid
throw Error {Status::FPOutOfRange, m_pos, len};
}
std::swap(m_pos, pos); // swap begin and end positions
// save string of number so it later can be reproduced
return TokenUniquePtr{new Token {pos, len, numStr, value, decimal}};
}


Expand Down
2 changes: 2 additions & 0 deletions parser.h
Expand Up @@ -25,6 +25,8 @@
#ifndef PARSER_H
#define PARSER_H

#include <sstream>

#include <QString>

#include "token.h"
Expand Down
6 changes: 3 additions & 3 deletions token.cpp
Expand Up @@ -49,10 +49,10 @@ std::unordered_map<Token::Type, int, EnumClassHash> Token::s_precendence {


// constructor to set double constants
Token::Token(int column, int length, QString string, double value,
Token::Token(int column, int length, const std::string string, double value,
bool decimal) : m_column{column}, m_length{length},
m_type{Token::Type::Constant}, m_string{string}, m_code{Invalid_Code},
m_reference{}, m_value{value}
m_type{Token::Type::Constant}, m_string{string.c_str()},
m_code{Invalid_Code}, m_reference{}, m_value{value}
{
if (value > std::numeric_limits<int>::min() - 0.5
&& value < std::numeric_limits<int>::max() + 0.5)
Expand Down
12 changes: 7 additions & 5 deletions token.h
Expand Up @@ -75,16 +75,18 @@ class Token
{}

// constructor for integer constants
Token(int column, int length, QString string, int value) : m_column{column},
m_length{length}, m_type{Token::Type::Constant},
m_dataType{DataType::Integer}, m_string{string}, m_code{Invalid_Code},
m_reference{}, m_subCode{None_SubCode}, m_valueInt{value}
Token(int column, int length, const std::string string, int value) :
m_column{column}, m_length{length}, m_type{Token::Type::Constant},
m_dataType{DataType::Integer}, m_string{string.c_str()},
m_code{Invalid_Code}, m_reference{}, m_subCode{None_SubCode},
m_valueInt{value}
{
m_value = value; // convert to double in case needed
}

// constructor for double constants
Token(int column, int length, QString string, double value, bool decimal);
Token(int column, int length, const std::string string, double value,
bool decimal);

// constructor for string constants
Token(int column, int length, QString string) : m_column{column},
Expand Down

0 comments on commit 8513875

Please sign in to comment.