/* -*- 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 #include #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_iterator i = std::upper_bound(linePositions.begin(), linePositions.end(), begin + pos); ASSERT(i != linePositions.begin()); return static_cast(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 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(); } // 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; }