228 lines
7.3 KiB
C++
228 lines
7.3 KiB
C++
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
|
*
|
|
* ***** BEGIN LICENSE BLOCK *****
|
|
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
|
*
|
|
* The contents of this file are subject to the Mozilla Public License Version
|
|
* 1.1 (the "License"); you may not use this file except in compliance with
|
|
* the License. You may obtain a copy of the License at
|
|
* http://www.mozilla.org/MPL/
|
|
*
|
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
* for the specific language governing rights and limitations under the
|
|
* License.
|
|
*
|
|
* The Original Code is the JavaScript 2 Prototype.
|
|
*
|
|
* The Initial Developer of the Original Code is
|
|
* Netscape Communications Corporation.
|
|
* Portions created by the Initial Developer are Copyright (C) 1998
|
|
* the Initial Developer. All Rights Reserved.
|
|
*
|
|
* Contributor(s):
|
|
*
|
|
* Alternatively, the contents of this file may be used under the terms of
|
|
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
|
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
|
* in which case the provisions of the GPL or the LGPL are applicable instead
|
|
* of those above. If you wish to allow use of your version of this file only
|
|
* under the terms of either the GPL or the LGPL, and not to allow others to
|
|
* use your version of this file under the terms of the MPL, indicate your
|
|
* decision by deleting the provisions above and replace them with the notice
|
|
* and other provisions required by the GPL or the LGPL. If you do not delete
|
|
* the provisions above, a recipient may use your version of this file under
|
|
* the terms of any one of the MPL, the GPL or the LGPL.
|
|
*
|
|
* ***** END LICENSE BLOCK ***** */
|
|
|
|
#include <algorithm>
|
|
#include <vector>
|
|
|
|
#include "systemtypes.h"
|
|
#include "js2value.h"
|
|
#include "strings.h"
|
|
|
|
#include "reader.h"
|
|
|
|
namespace JS = JavaScript;
|
|
|
|
|
|
// Create a Reader reading characters from the source string.
|
|
// sourceLocation describes the origin of the source and may be used for
|
|
// error messages. initialLineNum is the line number of the first line of the
|
|
// source string.
|
|
JS::Reader::Reader(const String &source, const String &sourceLocation, uint32 initialLineNum):
|
|
source(source + uni::null), sourceLocation(sourceLocation), initialLineNum(initialLineNum)
|
|
{
|
|
begin = p = this->source.data();
|
|
end = begin + this->source.size() - 1;
|
|
#ifdef DEBUG
|
|
recordString = 0;
|
|
#endif
|
|
beginLine();
|
|
}
|
|
|
|
|
|
// Mark the beginning of a line. Call this after reading every line break to
|
|
// fill out the line start table.
|
|
void JS::Reader::beginLine()
|
|
{
|
|
ASSERT(p <= end && (!linePositions.size() || p > linePositions.back()));
|
|
linePositions.push_back(p);
|
|
}
|
|
|
|
// Fully process the source in order to fill in the line start table.
|
|
void JS::Reader::fillLineStartsTable()
|
|
{
|
|
char16 ch;
|
|
do {
|
|
ch = get();
|
|
if (isLineBreak(ch))
|
|
beginLine();
|
|
} while (!getEof(ch));
|
|
}
|
|
|
|
// Return the number of the line containing the given character position.
|
|
// The line starts should have been recorded by calling beginLine.
|
|
uint32 JS::Reader::posToLineNum(size_t pos) const
|
|
{
|
|
ASSERT(pos <= getPos());
|
|
std::vector<const char16 *>::const_iterator i =
|
|
std::upper_bound(linePositions.begin(), linePositions.end(), begin + pos);
|
|
ASSERT(i != linePositions.begin());
|
|
|
|
return static_cast<uint32>(i-1 - linePositions.begin()) + initialLineNum;
|
|
}
|
|
|
|
|
|
// Return the character position as well as pointers to the beginning and end
|
|
// (not including the line terminator) of the nth line. If lineNum is out of
|
|
// range, return 0 and two nulls. The line starts should have been recorded by
|
|
// calling beginLine(). If the nth line is the last one recorded, then getLine
|
|
// manually finds the line ending by searching for a line break; otherwise,
|
|
// getLine assumes that the line ends one character before the beginning
|
|
// of the next line.
|
|
size_t JS::Reader::getLine(uint32 lineNum, const char16 *&lineBegin, const char16 *&lineEnd) const
|
|
{
|
|
lineBegin = 0;
|
|
lineEnd = 0;
|
|
if (lineNum < initialLineNum)
|
|
return 0;
|
|
lineNum -= initialLineNum;
|
|
if (lineNum >= linePositions.size())
|
|
return 0;
|
|
lineBegin = linePositions[lineNum];
|
|
|
|
const char16 *e;
|
|
++lineNum;
|
|
if (lineNum < linePositions.size())
|
|
e = linePositions[lineNum] - 1;
|
|
else {
|
|
e = lineBegin;
|
|
const char16 *end = Reader::end;
|
|
while (e != end && !isLineBreak(*e))
|
|
++e;
|
|
}
|
|
lineEnd = e;
|
|
return toSize_t(lineBegin - begin);
|
|
}
|
|
|
|
|
|
// Begin accumulating characters into the recordString, whose initial value is
|
|
// ignored and cleared. Each character passed to recordChar() is added to the
|
|
// end of the recordString. Recording ends when endRecord() or beginLine()
|
|
// is called. Recording is significantly optimized when the characters passed
|
|
// to readChar() are the same characters as read by get(). In this case the
|
|
// recorded String does not get allocated until endRecord() is called or a
|
|
// discrepancy appears between get() and recordChar().
|
|
void JS::Reader::beginRecording(String &recordString)
|
|
{
|
|
Reader::recordString = &recordString;
|
|
recordBase = p;
|
|
recordPos = p;
|
|
}
|
|
|
|
|
|
// Append ch to the recordString.
|
|
void JS::Reader::recordChar(char16 ch)
|
|
{
|
|
ASSERT(recordString);
|
|
if (recordPos) {
|
|
if (recordPos != end && *recordPos == ch) {
|
|
recordPos++;
|
|
return;
|
|
} else {
|
|
recordString->assign(recordBase, recordPos);
|
|
recordPos = 0;
|
|
}
|
|
}
|
|
*recordString += ch;
|
|
}
|
|
|
|
|
|
// Finish recording characters into the recordString that was last passed to
|
|
// beginRecording(). Return that recordString.
|
|
JS::String &JS::Reader::endRecording()
|
|
{
|
|
String *rs = recordString;
|
|
ASSERT(rs);
|
|
if (recordPos)
|
|
rs->assign(recordBase, recordPos);
|
|
recordString = 0;
|
|
return *rs;
|
|
}
|
|
|
|
|
|
// Report an error at the given character position in the source code.
|
|
void JS::Reader::error(Exception::Kind kind, const String &message, size_t pos)
|
|
{
|
|
uint32 lineNum = posToLineNum(pos);
|
|
const char16 *lineBegin;
|
|
const char16 *lineEnd;
|
|
size_t linePos = getLine(lineNum, lineBegin, lineEnd);
|
|
ASSERT(lineBegin && lineEnd && linePos <= pos);
|
|
|
|
throw Exception(kind, message, sourceLocation, lineNum, pos - linePos, pos, lineBegin, lineEnd);
|
|
}
|
|
|
|
|
|
// 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 <CR> and <CR><LF> sequences to <LF> characters; a <CR><LF> 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<char>(ch);
|
|
}
|
|
return str.size();
|
|
}
|
|
|
|
|
|
// Same as readLine of a string except that widens the resulting characters to Unicode.
|
|
size_t JS::LineReader::readLine(String &wstr)
|
|
{
|
|
string str;
|
|
size_t n = readLine(str);
|
|
wstr.resize(n);
|
|
std::transform(str.begin(), str.end(), wstr.begin(), widen);
|
|
return n;
|
|
}
|