You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
openmw/components/compiler/scanner.hpp

323 lines
7.9 KiB
C++

#ifndef COMPILER_SCANNER_H_INCLUDED
#define COMPILER_SCANNER_H_INCLUDED
#include <cctype>
#include <iosfwd>
#include <istream>
#include <string>
#include <vector>
#include "tokenloc.hpp"
namespace Compiler
{
class ErrorHandler;
class Parser;
class Extensions;
/// \brief Scanner
///
/// This class translate a char-stream to a token stream (delivered via
/// parser-callbacks).
class MultiChar
{
public:
MultiChar() { blank(); }
explicit MultiChar(const char ch)
{
blank();
mData[0] = ch;
mLength = getCharLength(ch);
}
static int getCharLength(const char ch)
{
unsigned char c = ch;
if (c <= 127)
return 0;
else if ((c & 0xE0) == 0xC0)
return 1;
else if ((c & 0xF0) == 0xE0)
return 2;
else if ((c & 0xF8) == 0xF0)
return 3;
else
return -1;
}
bool operator==(const char ch) const
{
return mData[0] == ch && mData[1] == 0 && mData[2] == 0 && mData[3] == 0;
}
bool operator==(const MultiChar& ch) const
{
return mData[0] == ch.mData[0] && mData[1] == ch.mData[1] && mData[2] == ch.mData[2]
&& mData[3] == ch.mData[3];
}
bool operator!=(const char ch) const
{
return mData[0] != ch || mData[1] != 0 || mData[2] != 0 || mData[3] != 0;
}
bool isWhitespace() const
{
return (mData[0] == ' ' || mData[0] == '\t' || mData[0] == ',') && mData[1] == 0 && mData[2] == 0
&& mData[3] == 0;
}
bool isDigit() const
{
return std::isdigit(static_cast<unsigned char>(mData[0])) && mData[1] == 0 && mData[2] == 0
&& mData[3] == 0;
}
bool isMinusSign() const
{
if (mData[0] == '-' && mData[1] == 0 && mData[2] == 0 && mData[3] == 0)
return true;
return mData[0] == '\xe2' && mData[1] == '\x80' && mData[2] == '\x93' && mData[3] == 0;
}
bool isAlpha() const
{
if (isMinusSign())
return false;
return std::isalpha(static_cast<unsigned char>(mData[0])) || mData[1] != 0 || mData[2] != 0
|| mData[3] != 0;
}
void appendTo(std::string& str) const
{
for (int i = 0; i <= mLength; i++)
str += mData[i];
}
void putback(std::istream& in) const
{
for (int i = mLength; i >= 0; i--)
in.putback(mData[i]);
}
bool getFrom(std::istream& in)
{
blank();
char ch = static_cast<char>(in.peek());
if (!in.good())
return false;
int length = getCharLength(ch);
if (length < 0)
return false;
for (int i = 0; i <= length; i++)
{
in.get(ch);
if (!in.good())
return false;
mData[i] = ch;
}
mLength = length;
return true;
}
bool peek(std::istream& in)
{
std::streampos p_orig = in.tellg();
char ch = static_cast<char>(in.peek());
if (!in.good())
return false;
int length = getCharLength(ch);
if (length < 0)
return false;
for (int i = 0; i <= length; i++)
{
in.get(ch);
if (!in.good())
return false;
mData[i] = ch;
}
mLength = length;
in.seekg(p_orig);
return true;
}
void blank()
{
std::fill(std::begin(mData), std::end(mData), '\0');
mLength = -1;
}
std::string data() const
{
// NB: mLength is the number of the last element in the array
return std::string(mData, mLength + 1);
}
private:
char mData[4]{};
int mLength{};
};
class Scanner
{
enum putback_type
{
Putback_None,
Putback_Special,
Putback_Integer,
Putback_Float,
Putback_Name,
Putback_Keyword
};
ErrorHandler& mErrorHandler;
TokenLoc mLoc;
TokenLoc mPrevLoc;
std::istream& mStream;
const Extensions* mExtensions;
putback_type mPutback;
int mPutbackCode;
int mPutbackInteger;
float mPutbackFloat;
std::string mPutbackName;
TokenLoc mPutbackLoc;
bool mStrictKeywords;
bool mTolerantNames;
bool mIgnoreNewline;
bool mExpectName;
bool mIgnoreSpecial;
public:
enum keyword
{
K_begin,
K_end,
K_short,
K_long,
K_float,
K_if,
K_endif,
K_else,
K_elseif,
K_while,
K_endwhile,
K_return,
K_messagebox,
K_set,
K_to
};
enum special
{
S_newline,
S_open,
S_close,
S_cmpEQ,
S_cmpNE,
S_cmpLT,
S_cmpLE,
S_cmpGT,
S_cmpGE,
S_plus,
S_minus,
S_mult,
S_div,
S_ref,
S_member
};
private:
// not implemented
Scanner(const Scanner&);
Scanner& operator=(const Scanner&);
bool get(MultiChar& c);
void putback(MultiChar& c);
bool scanToken(Parser& parser);
bool scanInt(MultiChar& c, Parser& parser, bool& cont);
bool scanFloat(const std::string& intValue, Parser& parser, bool& cont);
bool scanName(MultiChar& c, Parser& parser, bool& cont, std::string name = {});
/// \param name May contain the start of the name (one or more characters)
bool scanName(std::string& name);
bool scanSpecial(MultiChar& c, Parser& parser, bool& cont);
bool isStringCharacter(MultiChar& c, bool lookAhead = true);
public:
Scanner(ErrorHandler& errorHandler, std::istream& inputStream, const Extensions* extensions = nullptr);
///< constructor
void scan(Parser& parser);
///< Scan a token and deliver it to the parser.
void putbackSpecial(int code, const TokenLoc& loc);
///< put back a special token
void putbackInt(int value, const TokenLoc& loc);
///< put back an integer token
void putbackFloat(float value, const TokenLoc& loc);
///< put back a float token
void putbackName(const std::string& name, const TokenLoc& loc);
///< put back a name token
void putbackKeyword(int keyword, const TokenLoc& loc);
///< put back a keyword token
void listKeywords(std::vector<std::string>& keywords);
///< Append all known keywords to \a keywords.
/// Treat newline character as a part of script command.
///
/// \attention This mode lasts only until the next keyword is reached.
void enableIgnoreNewlines();
/// Do not accept keywords in quotation marks anymore.
///
/// \attention This mode lasts only until the next newline is reached.
void enableStrictKeywords();
/// Continue parsing a name when hitting a '.' or a '-'
///
/// \attention This mode lasts only until the next newline is reached.
void enableTolerantNames();
/// Treat '.' and '-' as the start of a name.
///
/// \attention This mode lasts only until the next newline is reached or the call to scan ends.
void enableExpectName();
};
}
#endif