From e8930953fc85f3eb5f3cfd2f12c697f2802bd586 Mon Sep 17 00:00:00 2001 From: "waldemar%netscape.com" Date: Thu, 6 Apr 2000 23:47:33 +0000 Subject: [PATCH] Implemented Formatters for output git-svn-id: svn://10.0.0.236/trunk@65459 18797224-902f-48f8-a5cc-f745e15eee43 --- mozilla/js/js2/js2.cpp | 151 ++++--------- mozilla/js/js2/numerics.cpp | 15 +- mozilla/js/js2/numerics.h | 6 +- mozilla/js/js2/utilities.cpp | 338 ++++++++++++++++++++++++---- mozilla/js2/src/numerics.cpp | 15 +- mozilla/js2/src/numerics.h | 6 +- mozilla/js2/src/utilities.cpp | 338 ++++++++++++++++++++++++---- mozilla/js2/tests/cpp/js2_shell.cpp | 151 ++++--------- 8 files changed, 698 insertions(+), 322 deletions(-) diff --git a/mozilla/js/js2/js2.cpp b/mozilla/js/js2/js2.cpp index afa2f48a1fd..ffcce6e486c 100644 --- a/mozilla/js/js2/js2.cpp +++ b/mozilla/js/js2/js2.cpp @@ -22,8 +22,6 @@ // JS2 shell. // -#include - #include "world.h" #include "interpreter.h" @@ -31,80 +29,7 @@ namespace JS = JavaScript; using namespace JavaScript; -#ifdef XP_MAC -#ifdef XP_MAC_MPW -/* Macintosh MPW replacements for the ANSI routines. These translate LF's to CR's because - the MPW libraries supplied by Metrowerks don't do that for some reason. */ -static void translateLFtoCR(char *str, int length) -{ - char *limit = str + length; - while (str != limit) { - if (*str == '\n') - *str = '\r'; - str++; - } -} - -int fputc(int c, FILE *file) -{ - char buffer = c; - if (buffer == '\n') - buffer = '\r'; - return fwrite(&buffer, 1, 1, file); -} - -int fputs(const char *s, FILE *file) -{ - char buffer[4096]; - int n = strlen(s); - int extra = 0; - - while (n > sizeof buffer) { - memcpy(buffer, s, sizeof buffer); - translateLFtoCR(buffer, sizeof buffer); - extra += fwrite(buffer, 1, sizeof buffer, file); - n -= sizeof buffer; - s += sizeof buffer; - } - memcpy(buffer, s, n); - translateLFtoCR(buffer, n); - return extra + fwrite(buffer, 1, n, file); -} - -int fprintf(FILE* file, const char *format, ...) -{ - va_list args; - char smallBuffer[4096]; - int n; - int bufferSize = sizeof smallBuffer; - char *buffer = smallBuffer; - int result; - - va_start(args, format); - n = vsnprintf(buffer, bufferSize, format, args); - va_end(args); - while (n < 0) { - if (buffer != smallBuffer) - free(buffer); - bufferSize <<= 1; - buffer = malloc(bufferSize); - if (!buffer) { - ASSERT(false); - return 0; - } - va_start(args, format); - n = vsnprintf(buffer, bufferSize, format, args); - va_end(args); - } - translateLFtoCR(buffer, n); - result = fwrite(buffer, 1, n, file); - if (buffer != smallBuffer) - free(buffer); - return result; -} - - -#else /* XP_MAC_MPW */ +#if defined(XP_MAC) && !defined(XP_MAC_MPW) #include #include @@ -118,65 +43,69 @@ static void initConsole(StringPtr consoleName, const char* startupMessage, int & // Set up a buffer for stderr (otherwise it's a pig). setvbuf(stderr, new char[BUFSIZ], _IOLBF, BUFSIZ); - std::cout << startupMessage; + stdOut << startupMessage; argc = 1; argv = mac_argv; } -#endif /* XP_MAC_MPW */ -#endif /* XP_MAC */ +#endif // Interactively read a line from the input stream in and put it into s. -static bool promptLine(istream &in, string &s, const char *prompt) +// Return false if reached the end of input before reading anything. +static bool promptLine(LineReader &inReader, string &s, const char *prompt) { - std::cout << prompt; -#ifdef XP_MAC_MPW - /* Print a CR after the prompt because MPW grabs the entire line when entering an interactive command */ - std::cout << std::endl; -#endif -#ifndef _WIN32 - return std::getline(in, s) != 0; -#else - char buffer[256]; - bool result = fgets(buffer, sizeof(buffer), stdin) != 0; - s = buffer; - return result; -#endif + if (prompt) { + stdOut << prompt; + #ifdef XP_MAC_MPW + // Print a CR after the prompt because MPW grabs the entire line when entering an interactive command. + stdOut << '\n'; + #endif + } + return inReader.readLine(s) != 0; } -static void readEvalPrint(istream &in, World &world) +const bool showTokens = true; + +static void readEvalPrint(FILE *in, World &world) { String buffer; string line; String sourceLocation = widenCString("console"); + LineReader inReader(in); - while (promptLine(in, line, buffer.empty() ? "js> " : "")) { - if (!buffer.empty()) - buffer += uni::lf; + while (promptLine(inReader, line, buffer.empty() ? "js> " : 0)) { appendChars(buffer, line.data(), line.size()); - if (!buffer.empty()) { - try { - Lexer l(world, buffer, sourceLocation); + try { + Arena a; + Parser p(world, a, buffer, sourceLocation); + + if (showTokens) { + Lexer &l = p.lexer; while (true) { const Token &t = l.get(true); if (t.hasKind(Token::end)) break; - String out; - out += ' '; - t.print(out, true); - showString(std::cout, out); + stdOut << ' '; + t.print(stdOut, true); } - } catch (Exception &e) { - std::cout << std::endl; - showString(std::cout, e.fullMessage()); + } else { + ExprNode *parseTree = p.parsePostfixExpression(); } - std::cout << std::endl; clear(buffer); + stdOut << '\n'; + } catch (Exception &e) { + // If we got a syntax error on the end of input, then wait for a continuation + // of input rather than printing the error message. + if (!(e.hasKind(Exception::syntaxError) && e.lineNum && e.pos == buffer.size() && + e.sourceFile == sourceLocation)) { + stdOut << '\n' << e.fullMessage(); + clear(buffer); + } } } - std::cout << std::endl; + stdOut << '\n'; #if 0 do { bufp = buffer; @@ -276,7 +205,7 @@ static int ProcessInputFile(JSContext *cx, JSObject *obj, char *filename) static int usage(void) { - std::cerr << "usage: js [-s] [-w] [-v version] [-f scriptfile] [scriptfile] [scriptarg...]\n"; + stdErr << "usage: js [-s] [-w] [-v version] [-f scriptfile] [scriptfile] [scriptarg...]\n"; return 2; } @@ -474,7 +403,7 @@ int main(int argc, char **argv) testInterpreter(5); testICG(world); #endif - readEvalPrint(std::cin, world); + readEvalPrint(stdin, world); return 0; //return ProcessArgs(argv + 1, argc - 1); diff --git a/mozilla/js/js2/numerics.cpp b/mozilla/js/js2/numerics.cpp index 2b8379c528a..c94a7fd864e 100644 --- a/mozilla/js/js2/numerics.cpp +++ b/mozilla/js/js2/numerics.cpp @@ -21,7 +21,6 @@ #include #include #include -#include #include "numerics.h" namespace JS = JavaScript; @@ -2735,10 +2734,22 @@ size_t JS::doubleToBaseStr(char *buffer, double value, uint base) // A version of doubleToStr that appends to the end of String dst. -void JS::printDouble(String &dst, double value, DToStrMode mode, int precision) +// precision should not exceed 101. +void JS::appendDouble(String &dst, double value, DToStrMode mode, int precision) { char buffer[dtosVariableBufferSize(101)]; ASSERT(uint(precision) <= 101); dst += doubleToStr(buffer, sizeof buffer, value, mode, precision); } + + +// A version of doubleToStr that prints to Formatter f. +// precision should not exceed 101. +void JS::printDouble(Formatter &f, double value, DToStrMode mode, int precision) +{ + char buffer[dtosVariableBufferSize(101)]; + ASSERT(uint(precision) <= 101); + + f << doubleToStr(buffer, sizeof buffer, value, mode, precision); +} diff --git a/mozilla/js/js2/numerics.h b/mozilla/js/js2/numerics.h index b8709e9c0d8..188758e66eb 100644 --- a/mozilla/js/js2/numerics.h +++ b/mozilla/js/js2/numerics.h @@ -139,7 +139,9 @@ namespace JavaScript { char *doubleToStr(char *buffer, size_t bufferSize, double value, DToStrMode mode, int precision); size_t doubleToBaseStr(char *buffer, double value, uint base); - void printDouble(String &dst, double value, DToStrMode mode = dtosStandard, int precision = 0); - inline String &operator+=(String &s, double value) {printDouble(s, value); return s;} + void appendDouble(String &dst, double value, DToStrMode mode = dtosStandard, int precision = 0); + inline String &operator+=(String &s, double value) {appendDouble(s, value); return s;} + void printDouble(Formatter &f, double value, DToStrMode mode = dtosStandard, int precision = 0); + inline Formatter &operator<<(Formatter &f, double value) {printDouble(f, value); return f;} } #endif diff --git a/mozilla/js/js2/utilities.cpp b/mozilla/js/js2/utilities.cpp index f84199a4327..e4e3533618d 100644 --- a/mozilla/js/js2/utilities.cpp +++ b/mozilla/js/js2/utilities.cpp @@ -20,7 +20,6 @@ #include #include #include -#include #include "utilities.h" #ifdef WIN32 @@ -1563,7 +1562,7 @@ struct JS::Arena::DestructorEntry: JS::ArenaObject { // Construct an Arena that allocates memory in chunks of the given size. -JS::Arena::Arena(size_t blockSize): blockSize(blockSize), freeBegin(0), freeEnd(0) +JS::Arena::Arena(size_t blockSize): freeBegin(0), freeEnd(0), blockSize(blockSize), destructorEntries(0) { ASSERT(blockSize && !(blockSize & basicAlignment-1)); rootDirectory.next = 0; @@ -1691,56 +1690,300 @@ JS::String &JS::newArenaString(Arena &arena, const String &str) // -// C++ I/O +// Output // -#ifdef __GNUC__ -JS::SaveFormat::SaveFormat(ostream &) {} - -JS::SaveFormat::~SaveFormat() {} -#else -JS::SaveFormat::SaveFormat(ostream &out): o(out), flags(out.flags()), fill(out.fill()) {} - -JS::SaveFormat::~SaveFormat() -{ - o.flags(flags); - o.fill(fill); -} -#endif - - -// Quotes for printing non-ASCII characters on an ASCII stream -#ifdef XP_MAC - const char beginUnprintable = char(0xC7); - const char endUnprintable = char(0xC8); -#else - const char beginUnprintable = '{'; - const char endUnprintable = '}'; -#endif - -void JS::showChar(ostream &out, char16 ch) -{ #ifdef XP_MAC_MPW - if (ch == '\n') - out << '\r'; - else -#endif - if (uchar16(ch) <= 0x7E && (uchar16(ch) >= ' ' || ch == '\n' || ch == '\t')) - out << static_cast(ch); - else { - SaveFormat sf(out); -#ifdef __GNUC__ - out << beginUnprintable << std::hex << std::setw(4) << std::setfill('0') << (uint16)ch << endUnprintable; -#else - out << beginUnprintable << std::hex << std::uppercase << std::setw(4) << std::setfill('0') << (uint16)ch << endUnprintable; -#endif +// Macintosh MPW replacements for the ANSI routines. These translate LF's to CR's because +// the MPW libraries supplied by Metrowerks don't do that for some reason. +static void translateLFtoCR(char *begin, char *end) +{ + while (begin != end) { + if (*begin == '\n') + *begin = '\r'; + ++begin; + } +} + +size_t JS::printChars(FILE *file, const char *begin, const char *end) +{ + ASSERT(end >= begin); + size_t n = static_cast(end - begin); + size_t extra = 0; + char buffer[1024]; + + while (n > sizeof buffer) { + memcpy(buffer, s, sizeof buffer); + translateLFtoCR(buffer, buffer + sizeof buffer); + extra += fwrite(buffer, 1, sizeof buffer, file); + n -= sizeof buffer; + s += sizeof buffer; + } + memcpy(buffer, s, n); + translateLFtoCR(buffer, buffer + n); + return extra + fwrite(buffer, 1, n, file); +} + +int std::fputc(int c, FILE *file) +{ + char buffer = c; + if (buffer == '\n') + buffer = '\r'; + return fwrite(&buffer, 1, 1, file); +} + +int std::fputs(const char *s, FILE *file) +{ + return static_cast(printChars(file, s, s + strlen(s))); +} + +int std::fprintf(FILE* file, const char *format, ...) +{ + Buffer b; + + while (true) { + va_list args; + va_start(args, format); + int n = vsnprintf(b.buffer, b.size, format, args); + va_end(args); + if (n >= 0 && n < b.size) { + translateLFtoCR(b.buffer, b.buffer + n); + return static_cast(fwrite(b.buffer, 1, n, file)); + } + b.expand(b.size*2); + } +} +#endif // XP_MAC_MPW + + + +static const printCharBufferSize = 64; + +// Print ch count times. +void JS::printChar(Formatter &f, char ch, int count) +{ + char str[printCharBufferSize]; + + while (count > 0) { + int c = count; + if (c > printCharBufferSize) + c = printCharBufferSize; + count -= c; + STD::memset(str, ch, static_cast(count)); + printString(f, str, str+c); } } -void JS::showString(ostream &out, const String &str) +// Print ch count times. +void JS::printChar(Formatter &f, char16 ch, int count) { - showString(out, str.begin(), str.end()); + char16 str[printCharBufferSize]; + + while (count > 0) { + int c = count; + if (c > printCharBufferSize) + c = printCharBufferSize; + count -= c; + char16 *strEnd = str + c; + std::fill(str, strEnd, ch); + printString(f, str, strEnd); + } +} + + +// Print i using the given formatting string, padding on the left with pad characters +// to use at least nDigits characters. +void JS::printNum(Formatter &f, uint32 i, int nDigits, char pad, const char *format) +{ + char str[20]; + int n = sprintf(str, format, i); + if (n < nDigits) + printChar(f, pad, nDigits - n); + printString(f, str, str+n); +} + + +// Print p as a pointer. +void JS::printPtr(Formatter &f, void *p) +{ + char str[20]; + int n = sprintf(str, "%p", p); + printString(f, str, str+n); +} + + +// printf formats for printing non-ASCII characters on an ASCII stream +#ifdef XP_MAC + static const char unprintableFormat[] = "\xC7%.4X\xC8"; // Use angle quotes +#elif defined _WIN32 + static const char unprintableFormat[] = "\xAB%.4X\xBB"; // Use angle quotes +#else + static const char unprintableFormat[] = "<%.4X>"; +#endif + + +static const uint16 defaultFilterRanges[] = { + 0x00, 0x09, // Filter all control characters except \t and \n + 0x0B, 0x20, + 0x7F, 0x100, // Filter all non-ASCII characters + 0, 0 +}; + +JS::BitSet<256> JS::AsciiFileFormatter::defaultFilter(defaultFilterRanges); + + +// Construct an AsciiFileFormatter using the given file and filter. +// If the filter is nil, use the default filter. +JS::AsciiFileFormatter::AsciiFileFormatter(FILE *file, BitSet<256> *filter): + file(file), filter(filter ? *filter : defaultFilter) +{ + filterEmpty = AsciiFileFormatter::filter.none(); +} + + +// Write ch, escaping non-ASCII characters. +void JS::AsciiFileFormatter::printChar8(char ch) +{ + if (filterChar(ch)) + fprintf(file, unprintableFormat, static_cast(ch)); + else + fputc(ch, file); +} + + +// Write ch, escaping non-ASCII characters. +void JS::AsciiFileFormatter::printChar16(char16 ch) +{ + if (filterChar(ch)) + fprintf(file, unprintableFormat, char16Value(ch)); + else + fputc(static_cast(ch), file); +} + + +// Write the null-terminated string str, escaping non-ASCII characters. +void JS::AsciiFileFormatter::printZStr8(const char *str) +{ + if (filterEmpty) + fputs(str, file); + else + printStr8(str, str + strlen(str)); +} + + +// Write the string between strBegin and strEnd, escaping non-ASCII characters. +void JS::AsciiFileFormatter::printStr8(const char *strBegin, const char *strEnd) +{ + if (filterEmpty) + printChars(file, strBegin, strEnd); + else { + ASSERT(strEnd >= strBegin); + const char *p = strBegin; + while (strBegin != strEnd) { + char ch = *strBegin; + if (filterChar(ch)) { + if (p != strBegin) { + printChars(file, p, strBegin); + p = strBegin; + } + fprintf(file, unprintableFormat, static_cast(ch)); + } + ++strBegin; + } + if (p != strBegin) + printChars(file, p, strBegin); + } +} + + +// Write the string between strBegin and strEnd, escaping non-ASCII characters. +void JS::AsciiFileFormatter::printStr16(const char16 *strBegin, const char16 *strEnd) +{ + char buffer[512]; + + ASSERT(strEnd >= strBegin); + char *q = buffer; + while (strBegin != strEnd) { + char16 ch = *strBegin++; + if (filterChar(ch)) { + if (q != buffer) { + printChars(file, buffer, q); + q = buffer; + } + fprintf(file, unprintableFormat, char16Value(ch)); + } else { + *q++ = static_cast(ch); + if (q == buffer + sizeof buffer) { + printChars(file, buffer, buffer + sizeof buffer); + q = buffer; + } + } + } + if (q != buffer) + printChars(file, buffer, q); +} + + +// Write the String s, escaping non-ASCII characters. +void JS::AsciiFileFormatter::printString16(const String &s) +{ + const char16 *begin = s.data(); + printStr16(begin, begin + s.size()); +} + + +// Write the printf format using the supplied args, escaping non-ASCII characters. +void JS::AsciiFileFormatter::printVFormat8(const char *format, va_list args) +{ + Buffer b; + + while (true) { + int n = vsnprintf(b.buffer, b.size, format, args); + if (n >= 0 && n < b.size) { + printStr8(b.buffer, b.buffer + n); + return; + } + b.expand(b.size*2); + } +} + +JS::AsciiFileFormatter JS::stdOut(stdout); +JS::AsciiFileFormatter JS::stdErr(stderr); + + +// +// Input +// + + +// Read a line from the input file, including the trailing line break character. +// Return the total number of characters read, which is str's length. +// Translate and sequences to characters; a sequence +// only counts as one character. +size_t JS::LineReader::readLine(string &str) +{ + int ch; + bool oldCRWasLast = crWasLast; + crWasLast = false; + + str.resize(0); + while ((ch = getc(in)) != EOF) { + if (ch == '\n') { + if (!str.size() && oldCRWasLast) + continue; + str += '\n'; + break; + } + if (ch == '\r') { + crWasLast = true; + str += '\n'; + break; + } + str += static_cast(ch); + } + + return str.size(); } @@ -1750,7 +1993,8 @@ void JS::showString(ostream &out, const String &str) static const char *const kindStrings[] = { - "Syntax error" // SyntaxError + "Syntax error", // syntaxError + "Stack overflow" // stackOverflow }; // Return a null-terminated string describing the exception's kind. @@ -1763,7 +2007,8 @@ const char *JS::Exception::kindString() const // Return the full error message. JS::String JS::Exception::fullMessage() const { - String m(sourceFile); + String m(widenCString("In ")); + m += sourceFile; if (lineNum) { char b[32]; sprintf(b, ", line %d:\n", lineNum); @@ -1779,6 +2024,7 @@ JS::String JS::Exception::fullMessage() const m += kindString(); m += ": "; m += message; + m += '\n'; return m; } diff --git a/mozilla/js2/src/numerics.cpp b/mozilla/js2/src/numerics.cpp index 2b8379c528a..c94a7fd864e 100644 --- a/mozilla/js2/src/numerics.cpp +++ b/mozilla/js2/src/numerics.cpp @@ -21,7 +21,6 @@ #include #include #include -#include #include "numerics.h" namespace JS = JavaScript; @@ -2735,10 +2734,22 @@ size_t JS::doubleToBaseStr(char *buffer, double value, uint base) // A version of doubleToStr that appends to the end of String dst. -void JS::printDouble(String &dst, double value, DToStrMode mode, int precision) +// precision should not exceed 101. +void JS::appendDouble(String &dst, double value, DToStrMode mode, int precision) { char buffer[dtosVariableBufferSize(101)]; ASSERT(uint(precision) <= 101); dst += doubleToStr(buffer, sizeof buffer, value, mode, precision); } + + +// A version of doubleToStr that prints to Formatter f. +// precision should not exceed 101. +void JS::printDouble(Formatter &f, double value, DToStrMode mode, int precision) +{ + char buffer[dtosVariableBufferSize(101)]; + ASSERT(uint(precision) <= 101); + + f << doubleToStr(buffer, sizeof buffer, value, mode, precision); +} diff --git a/mozilla/js2/src/numerics.h b/mozilla/js2/src/numerics.h index b8709e9c0d8..188758e66eb 100644 --- a/mozilla/js2/src/numerics.h +++ b/mozilla/js2/src/numerics.h @@ -139,7 +139,9 @@ namespace JavaScript { char *doubleToStr(char *buffer, size_t bufferSize, double value, DToStrMode mode, int precision); size_t doubleToBaseStr(char *buffer, double value, uint base); - void printDouble(String &dst, double value, DToStrMode mode = dtosStandard, int precision = 0); - inline String &operator+=(String &s, double value) {printDouble(s, value); return s;} + void appendDouble(String &dst, double value, DToStrMode mode = dtosStandard, int precision = 0); + inline String &operator+=(String &s, double value) {appendDouble(s, value); return s;} + void printDouble(Formatter &f, double value, DToStrMode mode = dtosStandard, int precision = 0); + inline Formatter &operator<<(Formatter &f, double value) {printDouble(f, value); return f;} } #endif diff --git a/mozilla/js2/src/utilities.cpp b/mozilla/js2/src/utilities.cpp index f84199a4327..e4e3533618d 100644 --- a/mozilla/js2/src/utilities.cpp +++ b/mozilla/js2/src/utilities.cpp @@ -20,7 +20,6 @@ #include #include #include -#include #include "utilities.h" #ifdef WIN32 @@ -1563,7 +1562,7 @@ struct JS::Arena::DestructorEntry: JS::ArenaObject { // Construct an Arena that allocates memory in chunks of the given size. -JS::Arena::Arena(size_t blockSize): blockSize(blockSize), freeBegin(0), freeEnd(0) +JS::Arena::Arena(size_t blockSize): freeBegin(0), freeEnd(0), blockSize(blockSize), destructorEntries(0) { ASSERT(blockSize && !(blockSize & basicAlignment-1)); rootDirectory.next = 0; @@ -1691,56 +1690,300 @@ JS::String &JS::newArenaString(Arena &arena, const String &str) // -// C++ I/O +// Output // -#ifdef __GNUC__ -JS::SaveFormat::SaveFormat(ostream &) {} - -JS::SaveFormat::~SaveFormat() {} -#else -JS::SaveFormat::SaveFormat(ostream &out): o(out), flags(out.flags()), fill(out.fill()) {} - -JS::SaveFormat::~SaveFormat() -{ - o.flags(flags); - o.fill(fill); -} -#endif - - -// Quotes for printing non-ASCII characters on an ASCII stream -#ifdef XP_MAC - const char beginUnprintable = char(0xC7); - const char endUnprintable = char(0xC8); -#else - const char beginUnprintable = '{'; - const char endUnprintable = '}'; -#endif - -void JS::showChar(ostream &out, char16 ch) -{ #ifdef XP_MAC_MPW - if (ch == '\n') - out << '\r'; - else -#endif - if (uchar16(ch) <= 0x7E && (uchar16(ch) >= ' ' || ch == '\n' || ch == '\t')) - out << static_cast(ch); - else { - SaveFormat sf(out); -#ifdef __GNUC__ - out << beginUnprintable << std::hex << std::setw(4) << std::setfill('0') << (uint16)ch << endUnprintable; -#else - out << beginUnprintable << std::hex << std::uppercase << std::setw(4) << std::setfill('0') << (uint16)ch << endUnprintable; -#endif +// Macintosh MPW replacements for the ANSI routines. These translate LF's to CR's because +// the MPW libraries supplied by Metrowerks don't do that for some reason. +static void translateLFtoCR(char *begin, char *end) +{ + while (begin != end) { + if (*begin == '\n') + *begin = '\r'; + ++begin; + } +} + +size_t JS::printChars(FILE *file, const char *begin, const char *end) +{ + ASSERT(end >= begin); + size_t n = static_cast(end - begin); + size_t extra = 0; + char buffer[1024]; + + while (n > sizeof buffer) { + memcpy(buffer, s, sizeof buffer); + translateLFtoCR(buffer, buffer + sizeof buffer); + extra += fwrite(buffer, 1, sizeof buffer, file); + n -= sizeof buffer; + s += sizeof buffer; + } + memcpy(buffer, s, n); + translateLFtoCR(buffer, buffer + n); + return extra + fwrite(buffer, 1, n, file); +} + +int std::fputc(int c, FILE *file) +{ + char buffer = c; + if (buffer == '\n') + buffer = '\r'; + return fwrite(&buffer, 1, 1, file); +} + +int std::fputs(const char *s, FILE *file) +{ + return static_cast(printChars(file, s, s + strlen(s))); +} + +int std::fprintf(FILE* file, const char *format, ...) +{ + Buffer b; + + while (true) { + va_list args; + va_start(args, format); + int n = vsnprintf(b.buffer, b.size, format, args); + va_end(args); + if (n >= 0 && n < b.size) { + translateLFtoCR(b.buffer, b.buffer + n); + return static_cast(fwrite(b.buffer, 1, n, file)); + } + b.expand(b.size*2); + } +} +#endif // XP_MAC_MPW + + + +static const printCharBufferSize = 64; + +// Print ch count times. +void JS::printChar(Formatter &f, char ch, int count) +{ + char str[printCharBufferSize]; + + while (count > 0) { + int c = count; + if (c > printCharBufferSize) + c = printCharBufferSize; + count -= c; + STD::memset(str, ch, static_cast(count)); + printString(f, str, str+c); } } -void JS::showString(ostream &out, const String &str) +// Print ch count times. +void JS::printChar(Formatter &f, char16 ch, int count) { - showString(out, str.begin(), str.end()); + char16 str[printCharBufferSize]; + + while (count > 0) { + int c = count; + if (c > printCharBufferSize) + c = printCharBufferSize; + count -= c; + char16 *strEnd = str + c; + std::fill(str, strEnd, ch); + printString(f, str, strEnd); + } +} + + +// Print i using the given formatting string, padding on the left with pad characters +// to use at least nDigits characters. +void JS::printNum(Formatter &f, uint32 i, int nDigits, char pad, const char *format) +{ + char str[20]; + int n = sprintf(str, format, i); + if (n < nDigits) + printChar(f, pad, nDigits - n); + printString(f, str, str+n); +} + + +// Print p as a pointer. +void JS::printPtr(Formatter &f, void *p) +{ + char str[20]; + int n = sprintf(str, "%p", p); + printString(f, str, str+n); +} + + +// printf formats for printing non-ASCII characters on an ASCII stream +#ifdef XP_MAC + static const char unprintableFormat[] = "\xC7%.4X\xC8"; // Use angle quotes +#elif defined _WIN32 + static const char unprintableFormat[] = "\xAB%.4X\xBB"; // Use angle quotes +#else + static const char unprintableFormat[] = "<%.4X>"; +#endif + + +static const uint16 defaultFilterRanges[] = { + 0x00, 0x09, // Filter all control characters except \t and \n + 0x0B, 0x20, + 0x7F, 0x100, // Filter all non-ASCII characters + 0, 0 +}; + +JS::BitSet<256> JS::AsciiFileFormatter::defaultFilter(defaultFilterRanges); + + +// Construct an AsciiFileFormatter using the given file and filter. +// If the filter is nil, use the default filter. +JS::AsciiFileFormatter::AsciiFileFormatter(FILE *file, BitSet<256> *filter): + file(file), filter(filter ? *filter : defaultFilter) +{ + filterEmpty = AsciiFileFormatter::filter.none(); +} + + +// Write ch, escaping non-ASCII characters. +void JS::AsciiFileFormatter::printChar8(char ch) +{ + if (filterChar(ch)) + fprintf(file, unprintableFormat, static_cast(ch)); + else + fputc(ch, file); +} + + +// Write ch, escaping non-ASCII characters. +void JS::AsciiFileFormatter::printChar16(char16 ch) +{ + if (filterChar(ch)) + fprintf(file, unprintableFormat, char16Value(ch)); + else + fputc(static_cast(ch), file); +} + + +// Write the null-terminated string str, escaping non-ASCII characters. +void JS::AsciiFileFormatter::printZStr8(const char *str) +{ + if (filterEmpty) + fputs(str, file); + else + printStr8(str, str + strlen(str)); +} + + +// Write the string between strBegin and strEnd, escaping non-ASCII characters. +void JS::AsciiFileFormatter::printStr8(const char *strBegin, const char *strEnd) +{ + if (filterEmpty) + printChars(file, strBegin, strEnd); + else { + ASSERT(strEnd >= strBegin); + const char *p = strBegin; + while (strBegin != strEnd) { + char ch = *strBegin; + if (filterChar(ch)) { + if (p != strBegin) { + printChars(file, p, strBegin); + p = strBegin; + } + fprintf(file, unprintableFormat, static_cast(ch)); + } + ++strBegin; + } + if (p != strBegin) + printChars(file, p, strBegin); + } +} + + +// Write the string between strBegin and strEnd, escaping non-ASCII characters. +void JS::AsciiFileFormatter::printStr16(const char16 *strBegin, const char16 *strEnd) +{ + char buffer[512]; + + ASSERT(strEnd >= strBegin); + char *q = buffer; + while (strBegin != strEnd) { + char16 ch = *strBegin++; + if (filterChar(ch)) { + if (q != buffer) { + printChars(file, buffer, q); + q = buffer; + } + fprintf(file, unprintableFormat, char16Value(ch)); + } else { + *q++ = static_cast(ch); + if (q == buffer + sizeof buffer) { + printChars(file, buffer, buffer + sizeof buffer); + q = buffer; + } + } + } + if (q != buffer) + printChars(file, buffer, q); +} + + +// Write the String s, escaping non-ASCII characters. +void JS::AsciiFileFormatter::printString16(const String &s) +{ + const char16 *begin = s.data(); + printStr16(begin, begin + s.size()); +} + + +// Write the printf format using the supplied args, escaping non-ASCII characters. +void JS::AsciiFileFormatter::printVFormat8(const char *format, va_list args) +{ + Buffer b; + + while (true) { + int n = vsnprintf(b.buffer, b.size, format, args); + if (n >= 0 && n < b.size) { + printStr8(b.buffer, b.buffer + n); + return; + } + b.expand(b.size*2); + } +} + +JS::AsciiFileFormatter JS::stdOut(stdout); +JS::AsciiFileFormatter JS::stdErr(stderr); + + +// +// Input +// + + +// Read a line from the input file, including the trailing line break character. +// Return the total number of characters read, which is str's length. +// Translate and sequences to characters; a sequence +// only counts as one character. +size_t JS::LineReader::readLine(string &str) +{ + int ch; + bool oldCRWasLast = crWasLast; + crWasLast = false; + + str.resize(0); + while ((ch = getc(in)) != EOF) { + if (ch == '\n') { + if (!str.size() && oldCRWasLast) + continue; + str += '\n'; + break; + } + if (ch == '\r') { + crWasLast = true; + str += '\n'; + break; + } + str += static_cast(ch); + } + + return str.size(); } @@ -1750,7 +1993,8 @@ void JS::showString(ostream &out, const String &str) static const char *const kindStrings[] = { - "Syntax error" // SyntaxError + "Syntax error", // syntaxError + "Stack overflow" // stackOverflow }; // Return a null-terminated string describing the exception's kind. @@ -1763,7 +2007,8 @@ const char *JS::Exception::kindString() const // Return the full error message. JS::String JS::Exception::fullMessage() const { - String m(sourceFile); + String m(widenCString("In ")); + m += sourceFile; if (lineNum) { char b[32]; sprintf(b, ", line %d:\n", lineNum); @@ -1779,6 +2024,7 @@ JS::String JS::Exception::fullMessage() const m += kindString(); m += ": "; m += message; + m += '\n'; return m; } diff --git a/mozilla/js2/tests/cpp/js2_shell.cpp b/mozilla/js2/tests/cpp/js2_shell.cpp index afa2f48a1fd..ffcce6e486c 100644 --- a/mozilla/js2/tests/cpp/js2_shell.cpp +++ b/mozilla/js2/tests/cpp/js2_shell.cpp @@ -22,8 +22,6 @@ // JS2 shell. // -#include - #include "world.h" #include "interpreter.h" @@ -31,80 +29,7 @@ namespace JS = JavaScript; using namespace JavaScript; -#ifdef XP_MAC -#ifdef XP_MAC_MPW -/* Macintosh MPW replacements for the ANSI routines. These translate LF's to CR's because - the MPW libraries supplied by Metrowerks don't do that for some reason. */ -static void translateLFtoCR(char *str, int length) -{ - char *limit = str + length; - while (str != limit) { - if (*str == '\n') - *str = '\r'; - str++; - } -} - -int fputc(int c, FILE *file) -{ - char buffer = c; - if (buffer == '\n') - buffer = '\r'; - return fwrite(&buffer, 1, 1, file); -} - -int fputs(const char *s, FILE *file) -{ - char buffer[4096]; - int n = strlen(s); - int extra = 0; - - while (n > sizeof buffer) { - memcpy(buffer, s, sizeof buffer); - translateLFtoCR(buffer, sizeof buffer); - extra += fwrite(buffer, 1, sizeof buffer, file); - n -= sizeof buffer; - s += sizeof buffer; - } - memcpy(buffer, s, n); - translateLFtoCR(buffer, n); - return extra + fwrite(buffer, 1, n, file); -} - -int fprintf(FILE* file, const char *format, ...) -{ - va_list args; - char smallBuffer[4096]; - int n; - int bufferSize = sizeof smallBuffer; - char *buffer = smallBuffer; - int result; - - va_start(args, format); - n = vsnprintf(buffer, bufferSize, format, args); - va_end(args); - while (n < 0) { - if (buffer != smallBuffer) - free(buffer); - bufferSize <<= 1; - buffer = malloc(bufferSize); - if (!buffer) { - ASSERT(false); - return 0; - } - va_start(args, format); - n = vsnprintf(buffer, bufferSize, format, args); - va_end(args); - } - translateLFtoCR(buffer, n); - result = fwrite(buffer, 1, n, file); - if (buffer != smallBuffer) - free(buffer); - return result; -} - - -#else /* XP_MAC_MPW */ +#if defined(XP_MAC) && !defined(XP_MAC_MPW) #include #include @@ -118,65 +43,69 @@ static void initConsole(StringPtr consoleName, const char* startupMessage, int & // Set up a buffer for stderr (otherwise it's a pig). setvbuf(stderr, new char[BUFSIZ], _IOLBF, BUFSIZ); - std::cout << startupMessage; + stdOut << startupMessage; argc = 1; argv = mac_argv; } -#endif /* XP_MAC_MPW */ -#endif /* XP_MAC */ +#endif // Interactively read a line from the input stream in and put it into s. -static bool promptLine(istream &in, string &s, const char *prompt) +// Return false if reached the end of input before reading anything. +static bool promptLine(LineReader &inReader, string &s, const char *prompt) { - std::cout << prompt; -#ifdef XP_MAC_MPW - /* Print a CR after the prompt because MPW grabs the entire line when entering an interactive command */ - std::cout << std::endl; -#endif -#ifndef _WIN32 - return std::getline(in, s) != 0; -#else - char buffer[256]; - bool result = fgets(buffer, sizeof(buffer), stdin) != 0; - s = buffer; - return result; -#endif + if (prompt) { + stdOut << prompt; + #ifdef XP_MAC_MPW + // Print a CR after the prompt because MPW grabs the entire line when entering an interactive command. + stdOut << '\n'; + #endif + } + return inReader.readLine(s) != 0; } -static void readEvalPrint(istream &in, World &world) +const bool showTokens = true; + +static void readEvalPrint(FILE *in, World &world) { String buffer; string line; String sourceLocation = widenCString("console"); + LineReader inReader(in); - while (promptLine(in, line, buffer.empty() ? "js> " : "")) { - if (!buffer.empty()) - buffer += uni::lf; + while (promptLine(inReader, line, buffer.empty() ? "js> " : 0)) { appendChars(buffer, line.data(), line.size()); - if (!buffer.empty()) { - try { - Lexer l(world, buffer, sourceLocation); + try { + Arena a; + Parser p(world, a, buffer, sourceLocation); + + if (showTokens) { + Lexer &l = p.lexer; while (true) { const Token &t = l.get(true); if (t.hasKind(Token::end)) break; - String out; - out += ' '; - t.print(out, true); - showString(std::cout, out); + stdOut << ' '; + t.print(stdOut, true); } - } catch (Exception &e) { - std::cout << std::endl; - showString(std::cout, e.fullMessage()); + } else { + ExprNode *parseTree = p.parsePostfixExpression(); } - std::cout << std::endl; clear(buffer); + stdOut << '\n'; + } catch (Exception &e) { + // If we got a syntax error on the end of input, then wait for a continuation + // of input rather than printing the error message. + if (!(e.hasKind(Exception::syntaxError) && e.lineNum && e.pos == buffer.size() && + e.sourceFile == sourceLocation)) { + stdOut << '\n' << e.fullMessage(); + clear(buffer); + } } } - std::cout << std::endl; + stdOut << '\n'; #if 0 do { bufp = buffer; @@ -276,7 +205,7 @@ static int ProcessInputFile(JSContext *cx, JSObject *obj, char *filename) static int usage(void) { - std::cerr << "usage: js [-s] [-w] [-v version] [-f scriptfile] [scriptfile] [scriptarg...]\n"; + stdErr << "usage: js [-s] [-w] [-v version] [-f scriptfile] [scriptfile] [scriptarg...]\n"; return 2; } @@ -474,7 +403,7 @@ int main(int argc, char **argv) testInterpreter(5); testICG(world); #endif - readEvalPrint(std::cin, world); + readEvalPrint(stdin, world); return 0; //return ProcessArgs(argv + 1, argc - 1);