Added string, character, and exception utilities

This commit is contained in:
waldemar%netscape.com 2000-01-25 22:58:34 +00:00
Родитель 88225dd675
Коммит cf7f6d77da
4 изменённых файлов: 372 добавлений и 130 удалений

Просмотреть файл

@ -17,8 +17,10 @@
// Copyright (C) 1998 Netscape Communications Corporation. All
// Rights Reserved.
#include <cstring>
#include <new>
#include <iomanip>
#include <algorithm>
#include "utilities.h"
#ifdef WIN32
@ -159,7 +161,52 @@ uint JS::floorLog2(uint32 n)
//
// From java.lang.Character.java:
// Return a String containing the characters of the null-terminated C string cstr
// (without the trailing null).
JS::String JS::widenCString(const char *cstr)
{
size_t len = std::strlen(cstr);
String s(len, uni::null);
std::transform(cstr, cstr+len, s.begin(), widen);
return s;
}
// Widen and append length characters starting at chars to the end of str.
void JS::appendChars(String &str, const char *chars, size_t length)
{
String::size_type strLen = str.size();
str.append(length, uni::null);
std::transform(chars, chars+length, str.begin()+strLen, widen);
}
// Widen and append the null-terminated string cstr to the end of str.
// Return str.
JS::String &JS::operator+=(String &str, const char *cstr)
{
appendChars(str, cstr, std::strlen(cstr));
return str;
}
// Return the concatenation of str and the null-terminated string cstr.
JS::String JS::operator+(const String &str, const char *cstr)
{
String s = widenCString(cstr);
return str + s;
}
// Return the concatenation of the null-terminated string cstr and str.
JS::String JS::operator+(const char *cstr, const String &str)
{
String s = widenCString(cstr);
return s += str;
}
// From java.lang.Character:
//
// The character properties are currently encoded into 32 bits in the
// following manner:
@ -1433,26 +1480,47 @@ const uint32 JS::CharInfo::a[] = {
// Return c converted to upper case. If c cannot be converted to upper case,
// return c unchanged.
JS::char16 JS::toUpper(char16 c)
char16 JS::toUpper(char16 c)
{
uint32 code = CharInfo::cCode(c);
if (code & 0x00100000)
c = static_cast<char16>(static_cast<int32>(c) - (static_cast<int32>(code) >> 22));
CharInfo ci(c);
if (ci.info & 0x00100000)
c = static_cast<char16>(static_cast<int32>(c) - (static_cast<int32>(ci.info) >> 22));
return c;
}
// Return c converted to lower case. If c cannot be converted to lower case,
// return c unchanged.
JS::char16 JS::toLower(char16 c)
char16 JS::toLower(char16 c)
{
uint32 code = CharInfo::cCode(c);
if (code & 0x00200000)
c = static_cast<char16>(static_cast<int32>(c) + (static_cast<int32>(code) >> 22));
CharInfo ci(c);
if (ci.info & 0x00200000)
c = static_cast<char16>(static_cast<int32>(c) + (static_cast<int32>(ci.info) >> 22));
return c;
}
// Return true if c is an ASCII hexadecimal digit, in which case store the digit's numeric value in d.
bool JS::isASCIIHexDigit(char16 c, uint &digit)
{
uint cv = c;
if (cv < '0')
return false;
if (cv <= '9') {
digit = cv - '0';
return true;
}
cv |= 0x20;
if (cv >= 'a' && cv <= 'f') {
digit = cv - 'a' + 10;
return true;
}
return false;
}
//
// C++ I/O
//
@ -1479,6 +1547,29 @@ void JS::showString(ostream &out, const String &str)
}
//
// Exceptions
//
static const char *const kindStrings[] = {
"Syntax error" // SyntaxError
};
// Return a null-terminated string describing the exception's kind.
const char *JS::Exception::kindString() const
{
return kindStrings[kind];
}
// Return the full error message.
JS::String JS::Exception::fullMessage() const
{
return kindString() + (": " + message);
}
//
// Static Initializers
//

Просмотреть файл

@ -25,6 +25,7 @@
#include <iostream>
#include "systemtypes.h"
using std::size_t;
using std::string;
using std::istream;
using std::ostream;
@ -72,25 +73,16 @@ namespace JavaScript {
// Unicode UTF-16 characters and strings
//
// A UTF-16 character
// Use wchar_t on platforms on which wchar_t has 16 bits; otherwise use uint16.
// Note that in C++ wchar_t is a distinct type rather than a typedef for some integral type.
// Like char, a char16 can be either signed or unsigned at the implementation's discretion.
typedef wchar_t char16;
typedef unsigned wchar_t uchar16;
// A string of UTF-16 characters. Nulls are allowed just like any other character.
// The string is not null-terminated.
// Use wstring if char16 is wchar_t. Otherwise use basic_string<uint16>.
typedef std::basic_string<char16> String;
#if 0
using std::wint_t;
const wint_t ueof = WEOF;
#else
typedef int32 wint_t; // A type that can hold any char16 plus one special value: ueof.
const wint_t ueof = -1;
#endif
typedef uint32 char16orEOF; // A type that can hold any char16 plus one special value: ueof.
const char16orEOF char16eof = static_cast<char16orEOF>(-1);
// If c is a char16, return it; if c is char16eof, return the character \uFFFF.
inline char16 char16orEOFToChar16(char16orEOF c) {return static_cast<char16>(c);}
// Special char16s
namespace uni {
@ -100,11 +92,20 @@ namespace JavaScript {
const char16 ls = 0x2028;
const char16 ps = 0x2029;
}
const uint16 firstFormatChar = 0x200C; // Lowest Unicode Cf character
inline char16 widen(char ch) {return static_cast<char16>(static_cast<uchar>(ch));}
String widenCString(const char *cstr);
void appendChars(String &str, const char *chars, size_t length);
String &operator+=(String &str, const char *cstr);
String operator+(const String &str, const char *cstr);
String operator+(const char *cstr, const String &str);
class CharInfo {
uint32 info; // Word from table a.
// Unicode character attribute lookup tables
static const uint8 x[];
static const uint8 y[];
@ -152,43 +153,49 @@ namespace JavaScript {
LineBreakGroup // 5 Line break character [LineBreakGroup & -2 == WhiteGroup]
};
// Character classifying and mapping macros, based on java.lang.Character
static uint32 cCode(char16 c) {return a[y[x[static_cast<uint16>(c)>>6]<<6 | c&0x3F]];}
static Type cType(char16 c) {return static_cast<Type>(cCode(c) & 0x1F);}
static Group cGroup(char16 c) {return static_cast<Group>(cCode(c) >> 16 & 7);}
CharInfo() {}
CharInfo(char16 c): info(a[y[x[static_cast<uint16>(c)>>6]<<6 | c&0x3F]]) {}
CharInfo(const CharInfo &ci): info(ci.info) {}
friend Type cType(const CharInfo &ci) {return static_cast<Type>(ci.info & 0x1F);}
friend Group cGroup(const CharInfo &ci) {return static_cast<Group>(ci.info >> 16 & 7);}
friend bool isAlpha(const CharInfo &ci)
{
return ((((1 << UppercaseLetter) | (1 << LowercaseLetter) | (1 << TitlecaseLetter) | (1 << ModifierLetter) | (1 << OtherLetter))
>> cType(ci)) & 1) != 0;
}
friend bool isAlphanumeric(const CharInfo &ci)
{
return ((((1 << UppercaseLetter) | (1 << LowercaseLetter) | (1 << TitlecaseLetter) | (1 << ModifierLetter) | (1 << OtherLetter) |
(1 << DecimalDigitNumber) | (1 << LetterNumber))
>> cType(ci)) & 1) != 0;
}
// Return true if this character can start a JavaScript identifier
friend bool isIdLeading(const CharInfo &ci) {return cGroup(ci) == IdGroup;}
// Return true if this character can continue a JavaScript identifier
friend bool isIdContinuing(const CharInfo &ci) {return cGroup(ci) & -2 == IdGroup;}
// Return true if this character is a Unicode decimal digit (Nd) character
friend bool isDecimalDigit(const CharInfo &ci) {return cType(ci) == DecimalDigitNumber;}
// Return true if this character is a Unicode white space or line break character
friend bool isSpace(const CharInfo &ci) {return cGroup(ci) & -2 == WhiteGroup;}
// Return true if this character is a Unicode line break character (LF, CR, LS, or PS)
friend bool isLineBreak(const CharInfo &ci) {return cGroup(ci) == LineBreakGroup;}
// Return true if this character is a Unicode format control character (Cf)
friend bool isFormat(const CharInfo &ci) {return cGroup(ci) == FormatGroup;}
friend bool isUpper(const CharInfo &ci) {return cType(ci) == UppercaseLetter;}
friend bool isLower(const CharInfo &ci) {return cType(ci) == LowercaseLetter;}
friend char16 toUpper(char16 c);
friend char16 toLower(char16 c);
};
inline bool isAlpha(char16 c)
{
return ((((1 << CharInfo::UppercaseLetter) | (1 << CharInfo::LowercaseLetter) | (1 << CharInfo::TitlecaseLetter) |
(1 << CharInfo::ModifierLetter) | (1 << CharInfo::OtherLetter))
>> CharInfo::cType(c)) & 1) != 0;
}
inline bool isAlphanumeric(char16 c)
{
return ((((1 << CharInfo::UppercaseLetter) | (1 << CharInfo::LowercaseLetter) | (1 << CharInfo::TitlecaseLetter) |
(1 << CharInfo::ModifierLetter) | (1 << CharInfo::OtherLetter) | (1 << CharInfo::DecimalDigitNumber))
>> CharInfo::cType(c)) & 1) != 0;
}
// Return true if c can start a JavaScript identifier
inline bool isIdLeading(char16 c) {return CharInfo::cGroup(c) == CharInfo::IdGroup;}
// Return true if c can continue a JavaScript identifier
inline bool isIdContinuing(char16 c) {return CharInfo::cGroup(c) & -2 == CharInfo::IdGroup;}
// Return true if c is a Unicode decimal digit (Nd) character
inline bool isDecimalDigit(char16 c) {return CharInfo::cType(c) == CharInfo::DecimalDigitNumber;}
// Return true if c is a Unicode white space or line break character
inline bool isSpace(char16 c) {return CharInfo::cGroup(c) & -2 == CharInfo::WhiteGroup;}
// Return true if c is a Unicode line break character (LF, CR, LS, or PS)
inline bool isLineBreak(char16 c) {return CharInfo::cGroup(c) == CharInfo::LineBreakGroup;}
inline bool isUpper(char16 c) {return CharInfo::cType(c) == CharInfo::UppercaseLetter;}
inline bool isLower(char16 c) {return CharInfo::cType(c) == CharInfo::LowercaseLetter;}
char16 toUpper(char16 c);
char16 toLower(char16 c);
inline bool isASCIIDecimalDigit(char16 c) {return c >= '0' && c <= '9';}
bool isASCIIHexDigit(char16 c, uint &digit);
//
@ -197,8 +204,7 @@ namespace JavaScript {
// private
template <typename T>
class ProtoArrayBuffer
{
class ProtoArrayBuffer {
protected:
T *buffer;
int32 length;
@ -237,8 +243,7 @@ namespace JavaScript {
// ArrayBuffer allocates the array from the heap.
// Use append to append nElts elements to the end of the ArrayBuffer.
template <typename T, int32 cacheSize>
class ArrayBuffer: public ProtoArrayBuffer<T>
{
class ArrayBuffer: public ProtoArrayBuffer<T> {
T cache[cacheSize];
public:
@ -283,8 +288,7 @@ namespace JavaScript {
// A class to remember the format of an ostream so that a function may modify it internally
// without changing it for the caller.
class SaveFormat
{
class SaveFormat {
ostream &o;
std::ios_base::fmtflags flags;
char fill;
@ -304,5 +308,31 @@ namespace JavaScript {
}
void showString(ostream &out, const String &str);
//
// Exceptions
//
// A JavaScript exception (other than out-of-memory, for which we use the standard C++
// exception bad_alloc).
struct Exception {
enum Kind {
SyntaxError
};
Kind kind; // The exception's kind
String message; // The detailed message
String sourceFile; // A description of the source code that caused the error
uint32 lineNum; // One-based source line number; 0 if unknown
uint32 charPos; // Zero-based character offset within the line
String sourceLine; // The text of the source line
Exception(Kind kind, const String &message): kind(kind), message(message), lineNum(0) {}
Exception(Kind kind, const String &message, const String &sourceFile, uint32 lineNum, uint32 charPos, const String sourceLine):
kind(kind), message(message), sourceFile(sourceFile), lineNum(lineNum), charPos(charPos), sourceLine(sourceLine) {}
const char *kindString() const;
String fullMessage() const;
};
}
#endif

Просмотреть файл

@ -17,8 +17,10 @@
// Copyright (C) 1998 Netscape Communications Corporation. All
// Rights Reserved.
#include <cstring>
#include <new>
#include <iomanip>
#include <algorithm>
#include "utilities.h"
#ifdef WIN32
@ -159,7 +161,52 @@ uint JS::floorLog2(uint32 n)
//
// From java.lang.Character.java:
// Return a String containing the characters of the null-terminated C string cstr
// (without the trailing null).
JS::String JS::widenCString(const char *cstr)
{
size_t len = std::strlen(cstr);
String s(len, uni::null);
std::transform(cstr, cstr+len, s.begin(), widen);
return s;
}
// Widen and append length characters starting at chars to the end of str.
void JS::appendChars(String &str, const char *chars, size_t length)
{
String::size_type strLen = str.size();
str.append(length, uni::null);
std::transform(chars, chars+length, str.begin()+strLen, widen);
}
// Widen and append the null-terminated string cstr to the end of str.
// Return str.
JS::String &JS::operator+=(String &str, const char *cstr)
{
appendChars(str, cstr, std::strlen(cstr));
return str;
}
// Return the concatenation of str and the null-terminated string cstr.
JS::String JS::operator+(const String &str, const char *cstr)
{
String s = widenCString(cstr);
return str + s;
}
// Return the concatenation of the null-terminated string cstr and str.
JS::String JS::operator+(const char *cstr, const String &str)
{
String s = widenCString(cstr);
return s += str;
}
// From java.lang.Character:
//
// The character properties are currently encoded into 32 bits in the
// following manner:
@ -1433,26 +1480,47 @@ const uint32 JS::CharInfo::a[] = {
// Return c converted to upper case. If c cannot be converted to upper case,
// return c unchanged.
JS::char16 JS::toUpper(char16 c)
char16 JS::toUpper(char16 c)
{
uint32 code = CharInfo::cCode(c);
if (code & 0x00100000)
c = static_cast<char16>(static_cast<int32>(c) - (static_cast<int32>(code) >> 22));
CharInfo ci(c);
if (ci.info & 0x00100000)
c = static_cast<char16>(static_cast<int32>(c) - (static_cast<int32>(ci.info) >> 22));
return c;
}
// Return c converted to lower case. If c cannot be converted to lower case,
// return c unchanged.
JS::char16 JS::toLower(char16 c)
char16 JS::toLower(char16 c)
{
uint32 code = CharInfo::cCode(c);
if (code & 0x00200000)
c = static_cast<char16>(static_cast<int32>(c) + (static_cast<int32>(code) >> 22));
CharInfo ci(c);
if (ci.info & 0x00200000)
c = static_cast<char16>(static_cast<int32>(c) + (static_cast<int32>(ci.info) >> 22));
return c;
}
// Return true if c is an ASCII hexadecimal digit, in which case store the digit's numeric value in d.
bool JS::isASCIIHexDigit(char16 c, uint &digit)
{
uint cv = c;
if (cv < '0')
return false;
if (cv <= '9') {
digit = cv - '0';
return true;
}
cv |= 0x20;
if (cv >= 'a' && cv <= 'f') {
digit = cv - 'a' + 10;
return true;
}
return false;
}
//
// C++ I/O
//
@ -1479,6 +1547,29 @@ void JS::showString(ostream &out, const String &str)
}
//
// Exceptions
//
static const char *const kindStrings[] = {
"Syntax error" // SyntaxError
};
// Return a null-terminated string describing the exception's kind.
const char *JS::Exception::kindString() const
{
return kindStrings[kind];
}
// Return the full error message.
JS::String JS::Exception::fullMessage() const
{
return kindString() + (": " + message);
}
//
// Static Initializers
//

Просмотреть файл

@ -25,6 +25,7 @@
#include <iostream>
#include "systemtypes.h"
using std::size_t;
using std::string;
using std::istream;
using std::ostream;
@ -72,25 +73,16 @@ namespace JavaScript {
// Unicode UTF-16 characters and strings
//
// A UTF-16 character
// Use wchar_t on platforms on which wchar_t has 16 bits; otherwise use uint16.
// Note that in C++ wchar_t is a distinct type rather than a typedef for some integral type.
// Like char, a char16 can be either signed or unsigned at the implementation's discretion.
typedef wchar_t char16;
typedef unsigned wchar_t uchar16;
// A string of UTF-16 characters. Nulls are allowed just like any other character.
// The string is not null-terminated.
// Use wstring if char16 is wchar_t. Otherwise use basic_string<uint16>.
typedef std::basic_string<char16> String;
#if 0
using std::wint_t;
const wint_t ueof = WEOF;
#else
typedef int32 wint_t; // A type that can hold any char16 plus one special value: ueof.
const wint_t ueof = -1;
#endif
typedef uint32 char16orEOF; // A type that can hold any char16 plus one special value: ueof.
const char16orEOF char16eof = static_cast<char16orEOF>(-1);
// If c is a char16, return it; if c is char16eof, return the character \uFFFF.
inline char16 char16orEOFToChar16(char16orEOF c) {return static_cast<char16>(c);}
// Special char16s
namespace uni {
@ -100,11 +92,20 @@ namespace JavaScript {
const char16 ls = 0x2028;
const char16 ps = 0x2029;
}
const uint16 firstFormatChar = 0x200C; // Lowest Unicode Cf character
inline char16 widen(char ch) {return static_cast<char16>(static_cast<uchar>(ch));}
String widenCString(const char *cstr);
void appendChars(String &str, const char *chars, size_t length);
String &operator+=(String &str, const char *cstr);
String operator+(const String &str, const char *cstr);
String operator+(const char *cstr, const String &str);
class CharInfo {
uint32 info; // Word from table a.
// Unicode character attribute lookup tables
static const uint8 x[];
static const uint8 y[];
@ -152,43 +153,49 @@ namespace JavaScript {
LineBreakGroup // 5 Line break character [LineBreakGroup & -2 == WhiteGroup]
};
// Character classifying and mapping macros, based on java.lang.Character
static uint32 cCode(char16 c) {return a[y[x[static_cast<uint16>(c)>>6]<<6 | c&0x3F]];}
static Type cType(char16 c) {return static_cast<Type>(cCode(c) & 0x1F);}
static Group cGroup(char16 c) {return static_cast<Group>(cCode(c) >> 16 & 7);}
CharInfo() {}
CharInfo(char16 c): info(a[y[x[static_cast<uint16>(c)>>6]<<6 | c&0x3F]]) {}
CharInfo(const CharInfo &ci): info(ci.info) {}
friend Type cType(const CharInfo &ci) {return static_cast<Type>(ci.info & 0x1F);}
friend Group cGroup(const CharInfo &ci) {return static_cast<Group>(ci.info >> 16 & 7);}
friend bool isAlpha(const CharInfo &ci)
{
return ((((1 << UppercaseLetter) | (1 << LowercaseLetter) | (1 << TitlecaseLetter) | (1 << ModifierLetter) | (1 << OtherLetter))
>> cType(ci)) & 1) != 0;
}
friend bool isAlphanumeric(const CharInfo &ci)
{
return ((((1 << UppercaseLetter) | (1 << LowercaseLetter) | (1 << TitlecaseLetter) | (1 << ModifierLetter) | (1 << OtherLetter) |
(1 << DecimalDigitNumber) | (1 << LetterNumber))
>> cType(ci)) & 1) != 0;
}
// Return true if this character can start a JavaScript identifier
friend bool isIdLeading(const CharInfo &ci) {return cGroup(ci) == IdGroup;}
// Return true if this character can continue a JavaScript identifier
friend bool isIdContinuing(const CharInfo &ci) {return cGroup(ci) & -2 == IdGroup;}
// Return true if this character is a Unicode decimal digit (Nd) character
friend bool isDecimalDigit(const CharInfo &ci) {return cType(ci) == DecimalDigitNumber;}
// Return true if this character is a Unicode white space or line break character
friend bool isSpace(const CharInfo &ci) {return cGroup(ci) & -2 == WhiteGroup;}
// Return true if this character is a Unicode line break character (LF, CR, LS, or PS)
friend bool isLineBreak(const CharInfo &ci) {return cGroup(ci) == LineBreakGroup;}
// Return true if this character is a Unicode format control character (Cf)
friend bool isFormat(const CharInfo &ci) {return cGroup(ci) == FormatGroup;}
friend bool isUpper(const CharInfo &ci) {return cType(ci) == UppercaseLetter;}
friend bool isLower(const CharInfo &ci) {return cType(ci) == LowercaseLetter;}
friend char16 toUpper(char16 c);
friend char16 toLower(char16 c);
};
inline bool isAlpha(char16 c)
{
return ((((1 << CharInfo::UppercaseLetter) | (1 << CharInfo::LowercaseLetter) | (1 << CharInfo::TitlecaseLetter) |
(1 << CharInfo::ModifierLetter) | (1 << CharInfo::OtherLetter))
>> CharInfo::cType(c)) & 1) != 0;
}
inline bool isAlphanumeric(char16 c)
{
return ((((1 << CharInfo::UppercaseLetter) | (1 << CharInfo::LowercaseLetter) | (1 << CharInfo::TitlecaseLetter) |
(1 << CharInfo::ModifierLetter) | (1 << CharInfo::OtherLetter) | (1 << CharInfo::DecimalDigitNumber))
>> CharInfo::cType(c)) & 1) != 0;
}
// Return true if c can start a JavaScript identifier
inline bool isIdLeading(char16 c) {return CharInfo::cGroup(c) == CharInfo::IdGroup;}
// Return true if c can continue a JavaScript identifier
inline bool isIdContinuing(char16 c) {return CharInfo::cGroup(c) & -2 == CharInfo::IdGroup;}
// Return true if c is a Unicode decimal digit (Nd) character
inline bool isDecimalDigit(char16 c) {return CharInfo::cType(c) == CharInfo::DecimalDigitNumber;}
// Return true if c is a Unicode white space or line break character
inline bool isSpace(char16 c) {return CharInfo::cGroup(c) & -2 == CharInfo::WhiteGroup;}
// Return true if c is a Unicode line break character (LF, CR, LS, or PS)
inline bool isLineBreak(char16 c) {return CharInfo::cGroup(c) == CharInfo::LineBreakGroup;}
inline bool isUpper(char16 c) {return CharInfo::cType(c) == CharInfo::UppercaseLetter;}
inline bool isLower(char16 c) {return CharInfo::cType(c) == CharInfo::LowercaseLetter;}
char16 toUpper(char16 c);
char16 toLower(char16 c);
inline bool isASCIIDecimalDigit(char16 c) {return c >= '0' && c <= '9';}
bool isASCIIHexDigit(char16 c, uint &digit);
//
@ -197,8 +204,7 @@ namespace JavaScript {
// private
template <typename T>
class ProtoArrayBuffer
{
class ProtoArrayBuffer {
protected:
T *buffer;
int32 length;
@ -237,8 +243,7 @@ namespace JavaScript {
// ArrayBuffer allocates the array from the heap.
// Use append to append nElts elements to the end of the ArrayBuffer.
template <typename T, int32 cacheSize>
class ArrayBuffer: public ProtoArrayBuffer<T>
{
class ArrayBuffer: public ProtoArrayBuffer<T> {
T cache[cacheSize];
public:
@ -283,8 +288,7 @@ namespace JavaScript {
// A class to remember the format of an ostream so that a function may modify it internally
// without changing it for the caller.
class SaveFormat
{
class SaveFormat {
ostream &o;
std::ios_base::fmtflags flags;
char fill;
@ -304,5 +308,31 @@ namespace JavaScript {
}
void showString(ostream &out, const String &str);
//
// Exceptions
//
// A JavaScript exception (other than out-of-memory, for which we use the standard C++
// exception bad_alloc).
struct Exception {
enum Kind {
SyntaxError
};
Kind kind; // The exception's kind
String message; // The detailed message
String sourceFile; // A description of the source code that caused the error
uint32 lineNum; // One-based source line number; 0 if unknown
uint32 charPos; // Zero-based character offset within the line
String sourceLine; // The text of the source line
Exception(Kind kind, const String &message): kind(kind), message(message), lineNum(0) {}
Exception(Kind kind, const String &message, const String &sourceFile, uint32 lineNum, uint32 charPos, const String sourceLine):
kind(kind), message(message), sourceFile(sourceFile), lineNum(lineNum), charPos(charPos), sourceLine(sourceLine) {}
const char *kindString() const;
String fullMessage() const;
};
}
#endif