Artifact 1b6d2c049d4b0d9639451f3f5cb6245f0bd30296:
- File Libs/StreamLib.js — part of check-in [0d9f1c65c4] at 2014-10-11 11:02:41 on branch trunk — Мелкие исправления в скриптах (user: sosnae size: 10427)
$engine JScript $uname StreamLib $dname Работа с данными в формате потока (stream) $addin global $addin stdlib //////////////////////////////////////////////////////////////////////////////////////// ////{ Cкрипт-библиотека StreamLib (StreamLib.js) для проекта "Снегопат" //// //// Описание: Реализует функционал по работе с данными во внутреннем //// формате 1С:Предприятия - "поток" (stream). //// //// Автор: Александр Кунташов <kuntashov@gmail.com>, http://compaud.ru/blog ////} //////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////// ////{ StreamFactory //// StreamFactory = {}; StreamFactory.CreateParser = function () { return new _StreamParser(); } StreamFactory.CreateSerializer = function () { return new _StreamSerializer(); } ////} StreamFactory //////////////////////////////////////////////////////////////////////////////////////// ////{ StreamParser //// function _StreamParser() { this._file = ''; this.setStream(''); } _StreamParser.prototype.setStream = function (stream) { this._stream = stream; this.pos = 0; this.len = this._stream.length; } _StreamParser.prototype.getStream = function() { return this._stream; } _StreamParser.prototype.readStreamFromFile = function(filepath) { this._file = v8New("File", filepath); if (!this._file.Exist()) { _logError("Файл не существует: " + this._file.FullName); return false; } var textDoc = v8New("TextDocument"); textDoc.Read(this._file.FullName); this.setStream(textDoc.GetText()); return true; } _StreamParser.prototype.parse = function() { try { return this._parse(); } catch (e) { Message(e.description); return null; } } //////////////////////////////////////////////////////////////////////////////////////// //// Реализация парсера StreamParser. ////{ _StreamParser.prototype._parse = function() { if (!this.atEnd()) { if (this.isEquals('{')) return this.readArray(); this.errorSyntaxError('_parse:1'); } this.errorUnexpectedEndOfStream('_parse:2'); } _StreamParser.prototype.readArray = function() { var a = []; while (this.next()) { if (this.isEquals('{')) { a.push(this.readArray()) } else if (this.isEquals('}')) { return a; } else if (this.isEquals('"')) { a.push(this.readString()); } else if (this.isNumber()) { a.push(this.readNumber()); } else if (this.isEquals(',') || this.isSpace()) { // TODO: проверять ошибку: две подряд идущие запятые. //this.errorSyntaxError('readArray:2, coma'); } else { this.errorSyntaxError('readArray:1'); } } this.errorUnexpectedEndOfStream('readArray:2'); } _StreamParser.prototype.readNumber = function() { var num = this.charAt(this.pos); oneCrapca = true; isExp = false; while (this.next()) { if (this.isNumber()) { num += this.charAt(this.pos); } else if (this.isEquals(',') || this.isEquals('}')) { this.pos--; return 1 * num; // Преобразуем к числу. } else if (this.isEquals('.') && oneCrapca) { oneCrapca = false; num += this.charAt(this.pos); } else if ((this.isEquals('e') || this.isEquals('-') || this.isEquals('+')) && (oneCrapca == false)){ debugger; //Message(this.charAt(this.pos)); //num += this.charAt(this.pos); isExp = true } //else if((this.isEquals('-') || this.isEquals('+')) && isExp){ //num += this.charAt(this.pos); //} else { this.errorSyntaxError('readNumber:1', 'цифра'); } } this.errorUnexpectedEndOfStream('readNumber:2'); }; _StreamParser.prototype.readString = function() { var str = ''; while (this.next()) { if (this.isEquals('"')) { if (!this.next()) this.errorUnexpectedEndOfStream('readString:1'); /* Проверим следующий символ после кавычки. В синтаксически верном файле потока следующим символом может быть: еще одна ковычка - это означает экранированную кавычку, запятая или закрывающая фигурная скобка или несколько пробельных символов, а потом запятая или закрывающая фигурная скобка - это значит строка закончилась. */ if (this.isEquals('"')) { str += '"'; } else { while (this.isSpace() && this.next()) ; // Пропускаем пробельные символы. if (this.atEnd()) this.errorUnexpectedEndOfStream('readString:3'); if (this.isEquals(',') || this.isEquals('}')) { this.pos--; return str; } this.errorSyntaxError('readString:4'); } } else { str += this.charAt(this.pos); } } debugger; this.errorSyntaxError('readString:6'); this.errorUnexpectedEndOfStream('readString:5'); } _StreamParser.prototype.atEnd = function () { return (this.pos == this._stream.length); }; _StreamParser.prototype.next = function () { this.pos++; if (this.atEnd()){ debugger; } return !this.atEnd(); }; _StreamParser.prototype.charAt = function (index) { return this._stream.charAt(index); }; _StreamParser.prototype.isEquals = function (ch) { if (this.atEnd()) return false; return (ch == this.charAt(this.pos)) }; _StreamParser.prototype.isSpace = function() { var ch = this.charAt(this.pos); return (ch == ' ' || ch == "\t" || ch == "\r" || ch == "\n"); }; _StreamParser.prototype.isNumber = function() { var ch = this.charAt(this.pos); return ch == '0' || ch == '1' || ch == '2' || ch == '3' || ch == '4' || ch == '5' || ch == '6' || ch == '7' || ch == '8' || ch == '9'; } //} Реализация парсера StreamParser. //////////////////////////////////////////////////////////////////////////////////////// //// Обработка ошибок алгоритма парсинга в StreamParser. ////{ function _StreamParserSyntaxErrorException(description, errorPos, methodId) { this.methodId = methodId; this.description = description; this.errorPos = errorPos; } function _StreamParserUnexpectedEndOfStreamExeption(description, methodId) { this.methodId = methodId; this.description = description; } _StreamParser.prototype.errorSyntaxError = function (methodId) { var desc = this._methodIdRepr(methodId) + "Ошибка разбора потока: синтаксическая ошибка в позиции [" + this.pos + "]: " + this._getErrorContext(); _logError(desc); throw new _StreamParserSyntaxErrorException(desc, this.pos, methodId); }, _StreamParser.prototype.errorUnexpectedEndOfStream = function(methodId) { var desc = this._methodIdRepr(methodId) + "Ошибка разбора потока: Неожиданный конец потока!"; _logError(desc); throw new _StreamParserUnexpectedEndOfStreamExeption(desc, methodId); } /* Возвращает контекст ошибки: строку, в которой произошла ошибки и строки выше и ниже этой строки. Перед позицией, в которой обнаружена ошибка будет добавлен маркер '<!>', выделенный дополнительно пробелами слева и справа (по одному пробелу). */ _StreamParser.prototype._getErrorContext = function() { var linesBefore = 1; var linesAfter = 1; var context = ' <!> '; // Маркер позиции ошибки. // Символы левее ошибки, включая одну строку выше строки с ошибкой. for (var i=this.pos-1; linesBefore >= 0 && i >= 0; i--) { var ch = this.charAt(i); context = ch + context; if (ch == "\n") linesBefore--; } // Символы правее ошибки, включая одну строку ниже строки с ошибкой. for (var i=this.pos; linesAfter >= 0 && i<this.len; i++) { var ch = this.charAt(i); context = context + ch; if (ch == "\n") linesAfter--; } return context; } _StreamParser.prototype._methodIdRepr = function(methodId) { return (methodId ? ("[" + methodId + "]: ") : "" ); } function _logError(message) { Message('StreamParser: ' + message); } //} Обработка ошибок алгоритма парсинга в StreamParser. ////} StreamParser