Source code

Revision control

Copy as Markdown

Other Tools

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef _MDB_
# include "mdb.h"
#endif
#ifndef _MORK_
# include "mork.h"
#endif
#ifndef _MORKNODE_
# include "morkNode.h"
#endif
#ifndef _MORKMAP_
# include "morkMap.h"
#endif
#ifndef _MORKENV_
# include "morkEnv.h"
#endif
#ifndef _MORKPARSER_
# include "morkParser.h"
#endif
#ifndef _MORKSTREAM_
# include "morkStream.h"
#endif
#ifndef _MORKBLOB_
# include "morkBlob.h"
#endif
#ifndef _MORKSINK_
# include "morkSink.h"
#endif
#ifndef _MORKCH_
# include "morkCh.h"
#endif
#ifndef _MORKSTORE_
# include "morkStore.h"
#endif
// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
// ````` ````` ````` ````` `````
// { ===== begin morkNode interface =====
/*public virtual*/ void morkParser::CloseMorkNode(
morkEnv* ev) // CloseParser() only if open
{
if (this->IsOpenNode()) {
this->MarkClosing();
this->CloseParser(ev);
this->MarkShut();
}
}
/*public virtual*/
morkParser::~morkParser() // assert CloseParser() executed earlier
{
MORK_ASSERT(mParser_Heap == 0);
MORK_ASSERT(mParser_Stream == 0);
}
/*public non-poly*/
morkParser::morkParser(morkEnv* ev, const morkUsage& inUsage,
nsIMdbHeap* ioHeap, morkStream* ioStream,
mdb_count inBytesPerParseSegment, nsIMdbHeap* ioSlotHeap)
: morkNode(ev, inUsage, ioHeap),
mParser_Heap(0),
mParser_Stream(0),
mParser_MoreGranularity(inBytesPerParseSegment),
mParser_State(morkParser_kStartState)
,
mParser_GroupContentStartPos(0)
,
mParser_TableMid(),
mParser_RowMid(),
mParser_CellMid()
,
mParser_InPort(morkBool_kFalse),
mParser_InDict(morkBool_kFalse),
mParser_InCell(morkBool_kFalse),
mParser_InMeta(morkBool_kFalse)
,
mParser_InPortRow(morkBool_kFalse),
mParser_InRow(morkBool_kFalse),
mParser_InTable(morkBool_kFalse),
mParser_InGroup(morkBool_kFalse)
,
mParser_AtomChange(morkChange_kNil),
mParser_CellChange(morkChange_kNil),
mParser_RowChange(morkChange_kNil),
mParser_TableChange(morkChange_kNil)
,
mParser_Change(morkChange_kNil),
mParser_IsBroken(morkBool_kFalse),
mParser_IsDone(morkBool_kFalse),
mParser_DoMore(morkBool_kTrue)
,
mParser_Mid()
,
mParser_ScopeCoil(ev, ioSlotHeap),
mParser_ValueCoil(ev, ioSlotHeap),
mParser_ColumnCoil(ev, ioSlotHeap),
mParser_StringCoil(ev, ioSlotHeap)
,
mParser_ScopeSpool(ev, &mParser_ScopeCoil),
mParser_ValueSpool(ev, &mParser_ValueCoil),
mParser_ColumnSpool(ev, &mParser_ColumnCoil),
mParser_StringSpool(ev, &mParser_StringCoil)
,
mParser_MidYarn(ev, morkUsage(morkUsage_kMember), ioSlotHeap) {
if (inBytesPerParseSegment < morkParser_kMinGranularity)
inBytesPerParseSegment = morkParser_kMinGranularity;
else if (inBytesPerParseSegment > morkParser_kMaxGranularity)
inBytesPerParseSegment = morkParser_kMaxGranularity;
mParser_MoreGranularity = inBytesPerParseSegment;
if (ioSlotHeap && ioStream) {
nsIMdbHeap_SlotStrongHeap(ioSlotHeap, ev, &mParser_Heap);
morkStream::SlotStrongStream(ioStream, ev, &mParser_Stream);
if (ev->Good()) {
mParser_Tag = morkParser_kTag;
mNode_Derived = morkDerived_kParser;
}
} else
ev->NilPointerError();
}
/*public non-poly*/ void morkParser::CloseParser(
morkEnv* ev) // called by CloseMorkNode();
{
if (this->IsNode()) {
if (!this->IsShutNode()) {
mParser_ScopeCoil.CloseCoil(ev);
mParser_ValueCoil.CloseCoil(ev);
mParser_ColumnCoil.CloseCoil(ev);
mParser_StringCoil.CloseCoil(ev);
nsIMdbHeap_SlotStrongHeap((nsIMdbHeap*)0, ev, &mParser_Heap);
morkStream::SlotStrongStream((morkStream*)0, ev, &mParser_Stream);
this->MarkShut();
}
} else
this->NonNodeError(ev);
}
// } ===== end morkNode methods =====
// ````` ````` ````` ````` `````
/*protected non-poly*/ void morkParser::NonGoodParserError(
morkEnv* ev) // when GoodParserTag() is false
{
ev->NewError("non-morkNode");
}
/*protected non-poly*/ void morkParser::NonUsableParserError(morkEnv* ev) //
{
if (this->IsNode()) {
if (this->IsOpenNode()) {
if (this->GoodParserTag()) {
// okay
} else
this->NonGoodParserError(ev);
} else
this->NonOpenNodeError(ev);
} else
this->NonNodeError(ev);
}
/*protected non-poly*/ void morkParser::StartParse(morkEnv* ev) {
MORK_USED_1(ev);
mParser_InCell = morkBool_kFalse;
mParser_InMeta = morkBool_kFalse;
mParser_InDict = morkBool_kFalse;
mParser_InPortRow = morkBool_kFalse;
mParser_RowMid.ClearMid();
mParser_TableMid.ClearMid();
mParser_CellMid.ClearMid();
mParser_GroupId = 0;
mParser_InPort = morkBool_kTrue;
mParser_GroupSpan.ClearSpan();
mParser_DictSpan.ClearSpan();
mParser_AliasSpan.ClearSpan();
mParser_MetaSpan.ClearSpan();
mParser_TableSpan.ClearSpan();
mParser_RowSpan.ClearSpan();
mParser_CellSpan.ClearSpan();
mParser_ColumnSpan.ClearSpan();
mParser_SlotSpan.ClearSpan();
mParser_PortSpan.ClearSpan();
}
/*protected non-poly*/ void morkParser::StopParse(morkEnv* ev) {
if (mParser_InCell) {
mParser_InCell = morkBool_kFalse;
mParser_CellSpan.SetEndWithEnd(mParser_PortSpan);
this->OnCellEnd(ev, mParser_CellSpan);
}
if (mParser_InMeta) {
mParser_InMeta = morkBool_kFalse;
mParser_MetaSpan.SetEndWithEnd(mParser_PortSpan);
this->OnMetaEnd(ev, mParser_MetaSpan);
}
if (mParser_InDict) {
mParser_InDict = morkBool_kFalse;
mParser_DictSpan.SetEndWithEnd(mParser_PortSpan);
this->OnDictEnd(ev, mParser_DictSpan);
}
if (mParser_InPortRow) {
mParser_InPortRow = morkBool_kFalse;
mParser_RowSpan.SetEndWithEnd(mParser_PortSpan);
this->OnPortRowEnd(ev, mParser_RowSpan);
}
if (mParser_InRow) {
mParser_InRow = morkBool_kFalse;
mParser_RowMid.ClearMid();
mParser_RowSpan.SetEndWithEnd(mParser_PortSpan);
this->OnRowEnd(ev, mParser_RowSpan);
}
if (mParser_InTable) {
mParser_InTable = morkBool_kFalse;
mParser_TableMid.ClearMid();
mParser_TableSpan.SetEndWithEnd(mParser_PortSpan);
this->OnTableEnd(ev, mParser_TableSpan);
}
if (mParser_GroupId) {
mParser_GroupId = 0;
mParser_GroupSpan.SetEndWithEnd(mParser_PortSpan);
this->OnGroupAbortEnd(ev, mParser_GroupSpan);
}
if (mParser_InPort) {
mParser_InPort = morkBool_kFalse;
this->OnPortEnd(ev, mParser_PortSpan);
}
}
int morkParser::eat_comment(morkEnv* ev) // last char was '/'
{
morkStream* s = mParser_Stream;
// Note morkStream::Getc() returns EOF when an error occurs, so
// we don't need to check for both c != EOF and ev->Good() below.
int c = s->Getc(ev);
if (c == '/') // C++ style comment?
{
while ((c = s->Getc(ev)) != EOF && c != 0xA && c != 0xD)
; /* empty */
if (c == 0xA || c == 0xD) c = this->eat_line_break(ev, c);
} else if (c == '*') /* C style comment? */
{
int depth = 1; // count depth of comments until depth reaches zero
while (depth > 0 && c != EOF) // still looking for comment end(s)?
{
while ((c = s->Getc(ev)) != EOF && c != '/' && c != '*') {
if (c == 0xA || c == 0xD) // need to count a line break?
{
c = this->eat_line_break(ev, c);
if (c == '/' || c == '*') break; // end while loop
}
}
if (c == '*') // maybe end of a comment, if next char is '/'?
{
if ((c = s->Getc(ev)) == '/') // end of comment?
{
--depth; // depth of comments has decreased by one
if (!depth) // comments all done?
c = s->Getc(ev); // return the byte after end of comment
} else if (c != EOF) // need to put the char back?
s->Ungetc(c); // especially need to put back '*', 0xA, or 0xD
} else if (c == '/') // maybe nested comemnt, if next char is '*'?
{
if ((c = s->Getc(ev)) == '*') // nested comment?
++depth; // depth of comments has increased by one
else if (c != EOF) // need to put the char back?
s->Ungetc(c); // especially need to put back '/', 0xA, or 0xD
}
if (ev->Bad()) c = EOF;
}
if (c == EOF && depth > 0) ev->NewWarning("EOF before end of comment");
} else
ev->NewWarning("expected / or *");
return c;
}
int morkParser::eat_line_break(morkEnv* ev, int inLast) {
morkStream* s = mParser_Stream;
int c = s->Getc(ev); // get next char after 0xA or 0xD
this->CountLineBreak();
if (c == 0xA || c == 0xD) // another line break character?
{
if (c != inLast) // not the same as the last one?
c = s->Getc(ev); // get next char after two-byte linebreak
}
return c;
}
int morkParser::eat_line_continue(morkEnv* ev) // last char was '\'
{
morkStream* s = mParser_Stream;
int c = s->Getc(ev);
if (c == 0xA || c == 0xD) // linebreak follows \ as expected?
{
c = this->eat_line_break(ev, c);
} else
ev->NewWarning("expected linebreak");
return c;
}
int morkParser::NextChar(morkEnv* ev) // next non-white content
{
morkStream* s = mParser_Stream;
int c = s->Getc(ev);
while (c > 0 && ev->Good()) {
if (c == '/')
c = this->eat_comment(ev);
else if (c == 0xA || c == 0xD)
c = this->eat_line_break(ev, c);
else if (c == '\\')
c = this->eat_line_continue(ev);
else if (morkCh_IsWhite(c))
c = s->Getc(ev);
else
break; // end while loop when return c is acceptable
}
if (ev->Bad()) {
mParser_State = morkParser_kBrokenState;
mParser_DoMore = morkBool_kFalse;
mParser_IsDone = morkBool_kTrue;
mParser_IsBroken = morkBool_kTrue;
c = EOF;
} else if (c == EOF) {
mParser_DoMore = morkBool_kFalse;
mParser_IsDone = morkBool_kTrue;
}
return c;
}
void morkParser::OnCellState(morkEnv* ev) { ev->StubMethodOnlyError(); }
void morkParser::OnMetaState(morkEnv* ev) { ev->StubMethodOnlyError(); }
void morkParser::OnRowState(morkEnv* ev) { ev->StubMethodOnlyError(); }
void morkParser::OnTableState(morkEnv* ev) { ev->StubMethodOnlyError(); }
void morkParser::OnDictState(morkEnv* ev) { ev->StubMethodOnlyError(); }
morkBuf* morkParser::ReadName(morkEnv* ev, int c) {
morkBuf* outBuf = 0;
if (!morkCh_IsName(c)) ev->NewError("not a name char");
morkCoil* coil = &mParser_ColumnCoil;
coil->ClearBufFill();
morkSpool* spool = &mParser_ColumnSpool;
spool->Seek(ev, /*pos*/ 0);
if (ev->Good()) {
spool->Putc(ev, c);
morkStream* s = mParser_Stream;
while ((c = s->Getc(ev)) != EOF && morkCh_IsMore(c) && ev->Good())
spool->Putc(ev, c);
if (ev->Good()) {
if (c != EOF) {
s->Ungetc(c);
spool->FlushSink(ev); // update coil->mBuf_Fill
} else
this->UnexpectedEofError(ev);
if (ev->Good()) outBuf = coil;
}
}
return outBuf;
}
mork_bool morkParser::ReadMid(morkEnv* ev, morkMid* outMid) {
outMid->ClearMid();
morkStream* s = mParser_Stream;
int next;
outMid->mMid_Oid.mOid_Id = this->ReadHex(ev, &next);
int c = next;
if (c == ':') {
if ((c = s->Getc(ev)) != EOF && ev->Good()) {
if (c == '^') {
outMid->mMid_Oid.mOid_Scope = this->ReadHex(ev, &next);
if (ev->Good()) s->Ungetc(next);
} else if (morkCh_IsName(c)) {
outMid->mMid_Buf = this->ReadName(ev, c);
} else
ev->NewError("expected name or hex after ':' following ID");
}
if (c == EOF && ev->Good()) this->UnexpectedEofError(ev);
} else
s->Ungetc(c);
return ev->Good();
}
void morkParser::ReadCell(morkEnv* ev) {
mParser_CellMid.ClearMid();
// this->StartSpanOnLastByte(ev, &mParser_CellSpan);
morkMid* cellMid = 0; // if mid syntax is used for column
morkBuf* cellBuf = 0; // if naked string is used for column
morkStream* s = mParser_Stream;
int c;
if ((c = s->Getc(ev)) != EOF && ev->Good()) {
// this->StartSpanOnLastByte(ev, &mParser_ColumnSpan);
if (c == '^') {
cellMid = &mParser_CellMid;
this->ReadMid(ev, cellMid);
// if ( !mParser_CellMid.mMid_Oid.mOid_Scope )
// mParser_CellMid.mMid_Oid.mOid_Scope = (mork_scope) 'c';
} else {
if (mParser_InMeta && c == morkStore_kFormColumn) {
ReadCellForm(ev, c);
return;
} else
cellBuf = this->ReadName(ev, c);
}
if (ev->Good()) {
// this->EndSpanOnThisByte(ev, &mParser_ColumnSpan);
mParser_InCell = morkBool_kTrue;
this->OnNewCell(ev, *mParser_CellSpan.AsPlace(), cellMid,
cellBuf); // , mParser_CellChange
mParser_CellChange = morkChange_kNil;
if ((c = this->NextChar(ev)) != EOF && ev->Good()) {
// this->StartSpanOnLastByte(ev, &mParser_SlotSpan);
if (c == '=') {
morkBuf* buf = this->ReadValue(ev);
if (buf) {
// this->EndSpanOnThisByte(ev, &mParser_SlotSpan);
this->OnValue(ev, mParser_SlotSpan, *buf);
}
} else if (c == '^') {
if (this->ReadMid(ev, &mParser_Mid)) {
// this->EndSpanOnThisByte(ev, &mParser_SlotSpan);
if ((c = this->NextChar(ev)) != EOF && ev->Good()) {
if (c != ')') ev->NewError("expected ')' after cell ^ID value");
} else if (c == EOF)
this->UnexpectedEofError(ev);
if (ev->Good()) this->OnValueMid(ev, mParser_SlotSpan, mParser_Mid);
}
} else if (c == 'r' || c == 't' || c == '"' || c == '\'') {
ev->NewError("cell syntax not yet supported");
} else {
ev->NewError("unknown cell syntax");
}
}
// this->EndSpanOnThisByte(ev, &mParser_CellSpan);
mParser_InCell = morkBool_kFalse;
this->OnCellEnd(ev, mParser_CellSpan);
}
}
mParser_CellChange = morkChange_kNil;
if (c == EOF && ev->Good()) this->UnexpectedEofError(ev);
}
void morkParser::ReadRowPos(morkEnv* ev) {
int c; // next character
mork_pos rowPos = this->ReadHex(ev, &c);
if (ev->Good() && c != EOF) // should put back byte after hex?
mParser_Stream->Ungetc(c);
this->OnRowPos(ev, rowPos);
}
void morkParser::ReadRow(morkEnv* ev, int c)
// zm:Row ::= zm:S? '[' zm:S? zm:Id zm:RowItem* zm:S? ']'
// zm:RowItem ::= zm:MetaRow | zm:Cell
// zm:MetaRow ::= zm:S? '[' zm:S? zm:Cell* zm:S? ']' /* meta attributes */
// zm:Cell ::= zm:S? '(' zm:Column zm:S? zm:Slot? ')'
{
if (ev->Good()) {
// this->StartSpanOnLastByte(ev, &mParser_RowSpan);
if (mParser_Change) mParser_RowChange = mParser_Change;
mork_bool cutAllRowCols = morkBool_kFalse;
if (c == '[') {
if ((c = this->NextChar(ev)) == '-')
cutAllRowCols = morkBool_kTrue;
else if (ev->Good() && c != EOF)
mParser_Stream->Ungetc(c);
if (this->ReadMid(ev, &mParser_RowMid)) {
mParser_InRow = morkBool_kTrue;
this->OnNewRow(ev, *mParser_RowSpan.AsPlace(), mParser_RowMid,
cutAllRowCols);
mParser_Change = mParser_RowChange = morkChange_kNil;
while ((c = this->NextChar(ev)) != EOF && ev->Good() && c != ']') {
switch (c) {
case '(': // cell
this->ReadCell(ev);
break;
case '[': // meta
this->ReadMeta(ev, ']');
break;
// case '+': // plus
// mParser_CellChange = morkChange_kAdd;
// break;
case '-': // minus
// mParser_CellChange = morkChange_kCut;
this->OnMinusCell(ev);
break;
// case '!': // bang
// mParser_CellChange = morkChange_kSet;
// break;
default:
ev->NewWarning("unexpected byte in row");
break;
} // switch
} // while
// XXX: What about already EOF and/or error here.
if (ev->Good()) {
if ((c = this->NextChar(ev)) == '!')
this->ReadRowPos(ev);
else if (c != EOF && ev->Good())
mParser_Stream->Ungetc(c);
}
// this->EndSpanOnThisByte(ev, &mParser_RowSpan);
mParser_InRow = morkBool_kFalse;
this->OnRowEnd(ev, mParser_RowSpan);
} // if ReadMid
} // if '['
else // c != '['
{
morkStream* s = mParser_Stream;
s->Ungetc(c);
if (this->ReadMid(ev, &mParser_RowMid)) {
mParser_InRow = morkBool_kTrue;
this->OnNewRow(ev, *mParser_RowSpan.AsPlace(), mParser_RowMid,
cutAllRowCols);
mParser_Change = mParser_RowChange = morkChange_kNil;
if (ev->Good()) {
if ((c = this->NextChar(ev)) == '!')
this->ReadRowPos(ev);
else if (c != EOF && ev->Good())
s->Ungetc(c);
}
// this->EndSpanOnThisByte(ev, &mParser_RowSpan);
mParser_InRow = morkBool_kFalse;
this->OnRowEnd(ev, mParser_RowSpan);
}
}
}
if (ev->Bad())
mParser_State = morkParser_kBrokenState;
else if (c == EOF)
mParser_State = morkParser_kDoneState;
}
void morkParser::ReadTable(morkEnv* ev)
// zm:Table ::= zm:S? '{' zm:S? zm:Id zm:TableItem* zm:S? '}'
// zm:TableItem ::= zm:MetaTable | zm:RowRef | zm:Row
// zm:MetaTable ::= zm:S? '{' zm:S? zm:Cell* zm:S? '}' /* meta attributes */
{
// this->StartSpanOnLastByte(ev, &mParser_TableSpan);
if (mParser_Change) mParser_TableChange = mParser_Change;
mork_bool cutAllTableRows = morkBool_kFalse;
int c = this->NextChar(ev);
if (c == '-')
cutAllTableRows = morkBool_kTrue;
else if (ev->Good() && c != EOF)
mParser_Stream->Ungetc(c);
// We should not call ReadMid() if c == EOF
if (c != EOF && ev->Good() && this->ReadMid(ev, &mParser_TableMid)) {
mParser_InTable = morkBool_kTrue;
this->OnNewTable(ev, *mParser_TableSpan.AsPlace(), mParser_TableMid,
cutAllTableRows);
mParser_Change = mParser_TableChange = morkChange_kNil;
while ((c = this->NextChar(ev)) != EOF && ev->Good() && c != '}') {
if (morkCh_IsHex(c)) {
this->ReadRow(ev, c);
} else {
switch (c) {
case '[': // row
this->ReadRow(ev, '[');
break;
case '{': // meta
this->ReadMeta(ev, '}');
break;
// case '+': // plus
// mParser_RowChange = morkChange_kAdd;
// break;
case '-': // minus
// mParser_RowChange = morkChange_kCut;
this->OnMinusRow(ev);
break;
// case '!': // bang
// mParser_RowChange = morkChange_kSet;
// break;
default:
ev->NewError("unexpected byte in table");
break;
}
}
}
// this->EndSpanOnThisByte(ev, &mParser_TableSpan);
mParser_InTable = morkBool_kFalse;
this->OnTableEnd(ev, mParser_TableSpan);
if (ev->Bad())
mParser_State = morkParser_kBrokenState;
else if (c == EOF)
mParser_State = morkParser_kDoneState;
return;
} // if
}
mork_id morkParser::ReadHex(morkEnv* ev, int* outNextChar)
// zm:Hex ::= [0-9a-fA-F] /* a single hex digit */
// zm:Hex+ ::= zm:Hex | zm:Hex zm:Hex+
{
mork_id hex = 0;
morkStream* s = mParser_Stream;
int c = this->NextChar(ev);
if (ev->Good()) {
if (c != EOF) {
if (morkCh_IsHex(c)) {
do {
if (morkCh_IsDigit(c)) // '0' through '9'?
c -= '0';
else if (morkCh_IsUpper(c)) // 'A' through 'F'?
c -= ('A' - 10); // c = (c - 'A') + 10;
else // 'a' through 'f'?
c -= ('a' - 10); // c = (c - 'a') + 10;
hex = (hex << 4) + c;
} while ((c = s->Getc(ev)) != EOF && ev->Good() && morkCh_IsHex(c));
} else
this->ExpectedHexDigitError(ev, c);
}
}
if (c == EOF) this->EofInsteadOfHexError(ev);
*outNextChar = c;
return hex;
}
/*static*/ void morkParser::EofInsteadOfHexError(morkEnv* ev) {
ev->NewWarning("eof instead of hex");
}
/*static*/ void morkParser::ExpectedHexDigitError(morkEnv* ev, int c) {
MORK_USED_1(c);
ev->NewWarning("expected hex digit");
}
/*static*/ void morkParser::ExpectedEqualError(morkEnv* ev) {
ev->NewWarning("expected '='");
}
/*static*/ void morkParser::UnexpectedEofError(morkEnv* ev) {
// This should be an error. (corrupted file system, etc.)
ev->NewError("unexpected eof");
}
morkBuf* morkParser::ReadValue(morkEnv* ev) {
morkBuf* outBuf = 0;
morkCoil* coil = &mParser_ValueCoil;
coil->ClearBufFill();
morkSpool* spool = &mParser_ValueSpool;
spool->Seek(ev, /*pos*/ 0);
if (ev->Good()) {
morkStream* s = mParser_Stream;
int c;
while ((c = s->Getc(ev)) != EOF && c != ')' && ev->Good()) {
if (c == '\\') // next char is escaped by '\'?
{
if ((c = s->Getc(ev)) == 0xA || c == 0xD) // linebreak after \?
{
c = this->eat_line_break(ev, c);
if (c == ')' || c == '\\' || c == '$') {
s->Ungetc(c); // just let while loop test read this again
continue; // goto next iteration of while loop
}
}
if (c == EOF || ev->Bad()) break; // end while loop
} else if (c == '$') // "$" escapes next two hex digits?
{
if ((c = s->Getc(ev)) != EOF && ev->Good()) {
mork_ch first = (mork_ch)c; // first hex digit
if ((c = s->Getc(ev)) != EOF && ev->Good()) {
mork_ch second = (mork_ch)c; // second hex digit
c = ev->HexToByte(first, second);
} else
break; // end while loop
} else
break; // end while loop
}
spool->Putc(ev, c);
}
if (ev->Good()) {
if (c != EOF)
spool->FlushSink(ev); // update coil->mBuf_Fill
else
this->UnexpectedEofError(ev);
if (ev->Good()) outBuf = coil;
}
}
return outBuf;
}
void morkParser::ReadDictForm(morkEnv* ev) {
int nextChar;
nextChar = this->NextChar(ev);
if (nextChar == '(') {
nextChar = this->NextChar(ev);
if (nextChar == morkStore_kFormColumn) {
int dictForm;
nextChar = this->NextChar(ev);
if (nextChar == '=') {
dictForm = this->NextChar(ev);
nextChar = this->NextChar(ev);
} else if (nextChar == '^') {
dictForm = this->ReadHex(ev, &nextChar);
} else {
ev->NewWarning("unexpected byte in dict form");
return;
}
mParser_ValueCoil.mText_Form = dictForm;
if (nextChar == ')') {
nextChar = this->NextChar(ev);
if (nextChar == '>') return;
}
}
}
ev->NewError("unexpected byte in dict form");
}
void morkParser::ReadCellForm(morkEnv* ev, int c) {
MORK_ASSERT(c == morkStore_kFormColumn);
int nextChar;
nextChar = this->NextChar(ev);
int cellForm;
if (nextChar == '=') {
cellForm = this->NextChar(ev);
nextChar = this->NextChar(ev);
} else if (nextChar == '^') {
cellForm = this->ReadHex(ev, &nextChar);
} else {
ev->NewError("unexpected byte in cell form");
return;
}
// ### not sure about this. Which form should we set?
// mBuilder_CellForm = mBuilder_RowForm = cellForm;
if (nextChar == ')') {
OnCellForm(ev, cellForm);
return;
}
ev->NewError("unexpected byte in cell form");
}
void morkParser::ReadAlias(morkEnv* ev)
// zm:Alias ::= zm:S? '(' ('#')? zm:Hex+ zm:S? zm:Value ')'
// zm:Value ::= '=' ([^)$\] | '\' zm:NonCRLF | zm:Continue | zm:Dollar)*
{
// this->StartSpanOnLastByte(ev, &mParser_AliasSpan);
int nextChar;
mork_id hex = this->ReadHex(ev, &nextChar);
int c = nextChar;
mParser_Mid.ClearMid();
mParser_Mid.mMid_Oid.mOid_Id = hex;
if (morkCh_IsWhite(c) && ev->Good()) c = this->NextChar(ev);
if (ev->Good()) {
if (c == '<') {
ReadDictForm(ev);
if (ev->Good()) c = this->NextChar(ev);
}
if (c == '=') {
mParser_Mid.mMid_Buf = this->ReadValue(ev);
if (mParser_Mid.mMid_Buf) {
// this->EndSpanOnThisByte(ev, &mParser_AliasSpan);
this->OnAlias(ev, mParser_AliasSpan, mParser_Mid);
// need to reset this somewhere.
mParser_ValueCoil.mText_Form = 0;
}
} else
this->ExpectedEqualError(ev);
}
}
void morkParser::ReadMeta(morkEnv* ev, int inEndMeta)
// zm:MetaDict ::= zm:S? '<' zm:S? zm:Cell* zm:S? '>' /* meta attributes */
// zm:MetaTable ::= zm:S? '{' zm:S? zm:Cell* zm:S? '}' /* meta attributes */
// zm:MetaRow ::= zm:S? '[' zm:S? zm:Cell* zm:S? ']' /* meta attributes */
{
// this->StartSpanOnLastByte(ev, &mParser_MetaSpan);
mParser_InMeta = morkBool_kTrue;
this->OnNewMeta(ev, *mParser_MetaSpan.AsPlace());
mork_bool more = morkBool_kTrue; // until end meta
int c;
while (more && (c = this->NextChar(ev)) != EOF && ev->Good()) {
switch (c) {
case '(': // cell
this->ReadCell(ev);
break;
case '>': // maybe end meta?
if (inEndMeta == '>')
more = morkBool_kFalse; // stop reading meta
else
this->UnexpectedByteInMetaWarning(ev);
break;
case '}': // maybe end meta?
if (inEndMeta == '}')
more = morkBool_kFalse; // stop reading meta
else
this->UnexpectedByteInMetaWarning(ev);
break;
case ']': // maybe end meta?
if (inEndMeta == ']')
more = morkBool_kFalse; // stop reading meta
else
this->UnexpectedByteInMetaWarning(ev);
break;
case '[': // maybe table meta row?
if (mParser_InTable)
this->ReadRow(ev, '[');
else
this->UnexpectedByteInMetaWarning(ev);
break;
default:
if (mParser_InTable && morkCh_IsHex(c))
this->ReadRow(ev, c);
else
this->UnexpectedByteInMetaWarning(ev);
break;
}
}
// this->EndSpanOnThisByte(ev, &mParser_MetaSpan);
mParser_InMeta = morkBool_kFalse;
this->OnMetaEnd(ev, mParser_MetaSpan);
}
/*static*/ void morkParser::UnexpectedByteInMetaWarning(morkEnv* ev) {
// This should be an error (maybe I/O error).
ev->NewError("unexpected byte in meta");
}
/*static*/ void morkParser::NonParserTypeError(morkEnv* ev) {
ev->NewError("non morkParser");
}
mork_bool morkParser::MatchPattern(morkEnv* ev, const char* inPattern) {
// if an error occurs, we want original inPattern in the debugger:
const char* pattern = inPattern; // mutable copy of pointer
morkStream* s = mParser_Stream;
int c;
while (*pattern && ev->Good()) {
char byte = *pattern++;
if ((c = s->Getc(ev)) != byte) {
ev->NewError("byte not in expected pattern");
}
}
return ev->Good();
}
mork_bool morkParser::FindGroupEnd(morkEnv* ev) {
mork_bool foundEnd = morkBool_kFalse;
// char gidBuf[ 64 ]; // to hold hex pattern we want
// (void) ev->TokenAsHex(gidBuf, mParser_GroupId);
morkStream* s = mParser_Stream;
int c;
while ((c = s->Getc(ev)) != EOF && ev->Good() && !foundEnd) {
if (c == '@') // maybe start of group ending?
{
// this->EndSpanOnThisByte(ev, &mParser_GroupSpan);
if ((c = s->Getc(ev)) == '$') // '$' follows '@' ?
{
if ((c = s->Getc(ev)) == '$') // '$' follows "@$" ?
{
if ((c = s->Getc(ev)) == '}') {
foundEnd = this->ReadEndGroupId(ev);
// this->EndSpanOnThisByte(ev, &mParser_GroupSpan);
} else
ev->NewError("expected '}' after @$$");
}
}
if (!foundEnd && c == '@') s->Ungetc(c);
}
}
return foundEnd && ev->Good();
}
void morkParser::ReadGroup(morkEnv* mev) {
nsIMdbEnv* ev = mev->AsMdbEnv();
int next = 0;
mParser_GroupId = this->ReadHex(mev, &next);
if (next == '{') {
morkStream* s = mParser_Stream;
int c;
if ((c = s->Getc(mev)) == '@') {
// we really need the following span inside morkBuilder::OnNewGroup():
this->StartSpanOnThisByte(mev, &mParser_GroupSpan);
mork_pos startPos = mParser_GroupSpan.mSpan_Start.mPlace_Pos;
// if ( !store->mStore_FirstCommitGroupPos )
// store->mStore_FirstCommitGroupPos = startPos;
// else if ( !store->mStore_SecondCommitGroupPos )
// store->mStore_SecondCommitGroupPos = startPos;
if (this->FindGroupEnd(mev)) {
mork_pos outPos;
s->Seek(ev, startPos, &outPos);
if (mev->Good()) {
this->OnNewGroup(mev, mParser_GroupSpan.mSpan_Start, mParser_GroupId);
// XXX What is the correct way to handle, false return from
// Readcontent?
// I think if this returns false, the next OnGroupCommitEnd should not
// be called, but rather this should be flagged one way or the other.
//
this->ReadContent(mev, /*inInsideGroup*/ morkBool_kTrue);
this->OnGroupCommitEnd(mev, mParser_GroupSpan);
}
}
} else
mev->NewError("expected '@' after @$${id{");
} else
mev->NewError("expected '{' after @$$id");
}
mork_bool morkParser::ReadAt(morkEnv* ev, mork_bool inInsideGroup)
/* groups must be ignored until properly terminated */
// zm:Group ::= zm:GroupStart zm:Content zm:GroupEnd /* transaction */
// zm:GroupStart ::= zm:S? '@$${' zm:Hex+ '{@' /* xaction id has own space */
// zm:GroupEnd ::= zm:GroupCommit | zm:GroupAbort
// zm:GroupCommit ::= zm:S? '@$$}' zm:Hex+ '}@' /* id matches start id */
// zm:GroupAbort ::= zm:S? '@$$}~~}@' /* id matches start id */
/* We must allow started transactions to be aborted in summary files. */
/* Note '$$' will never occur unescaped in values we will see in Mork. */
{
if (this->MatchPattern(ev, "$$")) {
morkStream* s = mParser_Stream;
int c;
if (((c = s->Getc(ev)) == '{' || c == '}') && ev->Good()) {
if (c == '{') // start of new group?
{
if (!inInsideGroup)
this->ReadGroup(ev);
else
ev->NewError("nested @$${ inside another group");
} else // c == '}' // end of old group?
{
if (inInsideGroup) {
this->ReadEndGroupId(ev);
mParser_GroupId = 0;
} else
ev->NewError("unmatched @$$} outside any group");
}
} else
ev->NewError("expected '{' or '}' after @$$");
}
return ev->Good();
}
mork_bool morkParser::ReadEndGroupId(morkEnv* ev) {
mork_bool outSawGroupId = morkBool_kFalse;
morkStream* s = mParser_Stream;
int c;
if ((c = s->Getc(ev)) != EOF && ev->Good()) {
if (c == '~') // transaction is aborted?
{
this->MatchPattern(ev, "~}@"); // finish rest of pattern
} else // push back byte and read expected trailing hex id
{
s->Ungetc(c);
int next = 0;
mork_gid endGroupId = this->ReadHex(ev, &next);
if (ev->Good()) {
if (endGroupId == mParser_GroupId) // matches start?
{
if (next == '}') // '}' after @$$}id ?
{
if ((c = s->Getc(ev)) == '@') // '@' after @$$}id} ?
{
// looks good, so return with no error
outSawGroupId = morkBool_kTrue;
mParser_InGroup = false;
} else
ev->NewError("expected '@' after @$$}id}");
} else
ev->NewError("expected '}' after @$$}id");
} else
ev->NewError("end group id mismatch");
}
}
}
return (outSawGroupId && ev->Good());
}
void morkParser::ReadDict(morkEnv* ev)
// zm:Dict ::= zm:S? '<' zm:DictItem* zm:S? '>'
// zm:DictItem ::= zm:MetaDict | zm:Alias
// zm:MetaDict ::= zm:S? '<' zm:S? zm:Cell* zm:S? '>' /* meta attributes */
// zm:Alias ::= zm:S? '(' ('#')? zm:Hex+ zm:S? zm:Value ')'
{
mParser_Change = morkChange_kNil;
mParser_AtomChange = morkChange_kNil;
// this->StartSpanOnLastByte(ev, &mParser_DictSpan);
mParser_InDict = morkBool_kTrue;
this->OnNewDict(ev, *mParser_DictSpan.AsPlace());
int c;
while ((c = this->NextChar(ev)) != EOF && ev->Good() && c != '>') {
switch (c) {
case '(': // alias
this->ReadAlias(ev);
break;
case '<': // meta
this->ReadMeta(ev, '>');
break;
default:
ev->NewError("unexpected byte in dict");
break;
}
}
// this->EndSpanOnThisByte(ev, &mParser_DictSpan);
mParser_InDict = morkBool_kFalse;
this->OnDictEnd(ev, mParser_DictSpan);
if (ev->Bad())
mParser_State = morkParser_kBrokenState;
else if (c == EOF)
mParser_State = morkParser_kDoneState;
}
void morkParser::EndSpanOnThisByte(morkEnv* mev, morkSpan* ioSpan) {
mork_pos here;
nsIMdbEnv* ev = mev->AsMdbEnv();
nsresult rv = mParser_Stream->Tell(ev, &here);
if (NS_SUCCEEDED(rv) && mev->Good()) {
this->SetHerePos(here);
ioSpan->SetEndWithEnd(mParser_PortSpan);
}
}
void morkParser::EndSpanOnLastByte(morkEnv* mev, morkSpan* ioSpan) {
mork_pos here;
nsIMdbEnv* ev = mev->AsMdbEnv();
nsresult rv = mParser_Stream->Tell(ev, &here);
if (NS_SUCCEEDED(rv) && mev->Good()) {
if (here > 0)
--here;
else
here = 0;
this->SetHerePos(here);
ioSpan->SetEndWithEnd(mParser_PortSpan);
}
}
void morkParser::StartSpanOnLastByte(morkEnv* mev, morkSpan* ioSpan) {
mork_pos here;
nsIMdbEnv* ev = mev->AsMdbEnv();
nsresult rv = mParser_Stream->Tell(ev, &here);
if (NS_SUCCEEDED(rv) && mev->Good()) {
if (here > 0)
--here;
else
here = 0;
this->SetHerePos(here);
ioSpan->SetStartWithEnd(mParser_PortSpan);
ioSpan->SetEndWithEnd(mParser_PortSpan);
}
}
void morkParser::StartSpanOnThisByte(morkEnv* mev, morkSpan* ioSpan) {
mork_pos here;
nsIMdbEnv* ev = mev->AsMdbEnv();
nsresult rv = mParser_Stream->Tell(ev, &here);
if (NS_SUCCEEDED(rv) && mev->Good()) {
this->SetHerePos(here);
ioSpan->SetStartWithEnd(mParser_PortSpan);
ioSpan->SetEndWithEnd(mParser_PortSpan);
}
}
// What happens if ReadContent returns false, i.e., could not grok the
// content?
mork_bool morkParser::ReadContent(morkEnv* ev, mork_bool inInsideGroup) {
int c;
mork_bool keep_going = true;
while (keep_going && (c = this->NextChar(ev)) != EOF && ev->Good()) {
switch (c) {
case '[': // row
this->ReadRow(ev, '[');
keep_going = false;
break;
case '{': // table
this->ReadTable(ev);
keep_going = false;
break;
case '<': // dict
this->ReadDict(ev);
keep_going = false;
break;
case '@': // group
return this->ReadAt(ev, inInsideGroup);
// break;
// case '+': // plus
// mParser_Change = morkChange_kAdd;
// break;
// case '-': // minus
// mParser_Change = morkChange_kCut;
// break;
// case '!': // bang
// mParser_Change = morkChange_kSet;
// break;
default:
ev->NewError("unexpected byte in ReadContent()");
break;
}
}
if (ev->Bad())
mParser_State = morkParser_kBrokenState;
else if (c == EOF)
mParser_State = morkParser_kDoneState;
return (ev->Good() && c != EOF);
}
void morkParser::OnPortState(morkEnv* ev) {
mork_bool firstTime = !mParser_InPort;
mParser_InPort = morkBool_kTrue;
if (firstTime) this->OnNewPort(ev, *mParser_PortSpan.AsPlace());
mork_bool done = !this->ReadContent(ev, mParser_InGroup /*inInsideGroup*/);
if (done) {
mParser_InPort = morkBool_kFalse;
this->OnPortEnd(ev, mParser_PortSpan);
}
if (ev->Bad()) mParser_State = morkParser_kBrokenState;
}
void morkParser::OnStartState(morkEnv* mev) {
morkStream* s = mParser_Stream;
nsIMdbEnv* ev = mev->AsMdbEnv();
if (s && s->IsNode() && s->IsOpenNode()) {
mork_pos outPos;
nsresult rv = s->Seek(ev, 0, &outPos);
if (NS_SUCCEEDED(rv) && mev->Good()) {
this->StartParse(mev);
mParser_State = morkParser_kPortState;
}
} else
mev->NilPointerError();
if (mev->Bad()) mParser_State = morkParser_kBrokenState;
}
/*protected non-poly*/ void morkParser::ParseChunk(morkEnv* ev) {
mParser_Change = morkChange_kNil;
mParser_DoMore = morkBool_kTrue;
switch (mParser_State) {
case morkParser_kCellState: // 0
this->OnCellState(ev);
break;
case morkParser_kMetaState: // 1
this->OnMetaState(ev);
break;
case morkParser_kRowState: // 2
this->OnRowState(ev);
break;
case morkParser_kTableState: // 3
this->OnTableState(ev);
break;
case morkParser_kDictState: // 4
this->OnDictState(ev);
break;
case morkParser_kPortState: // 5
this->OnPortState(ev);
break;
case morkParser_kStartState: // 6
this->OnStartState(ev);
break;
case morkParser_kDoneState: // 7
mParser_DoMore = morkBool_kFalse;
mParser_IsDone = morkBool_kTrue;
this->StopParse(ev);
break;
case morkParser_kBrokenState: // 8
mParser_DoMore = morkBool_kFalse;
mParser_IsBroken = morkBool_kTrue;
this->StopParse(ev);
break;
default: // ?
MORK_ASSERT(morkBool_kFalse);
mParser_State = morkParser_kBrokenState;
break;
}
}
/*public non-poly*/ mdb_count
morkParser::ParseMore( // return count of bytes consumed now
morkEnv* ev, // context
mork_pos* outPos, // current byte pos in the stream afterwards
mork_bool* outDone, // is parsing finished?
mork_bool* outBroken // is parsing irreparably dead and broken?
) {
mdb_count outCount = 0;
if (this->IsNode() && this->GoodParserTag() && this->IsOpenNode()) {
mork_pos startPos = this->HerePos();
if (!mParser_IsDone && !mParser_IsBroken) this->ParseChunk(ev);
// HerePos is only updated for groups. I'd like it to be more accurate.
mork_pos here;
mParser_Stream->Tell(ev, &here);
if (outDone) *outDone = mParser_IsDone;
if (outBroken) *outBroken = mParser_IsBroken;
if (outPos) *outPos = here;
if (here > startPos) outCount = (mdb_count)(here - startPos);
} else {
this->NonUsableParserError(ev);
if (outDone) *outDone = morkBool_kTrue;
if (outBroken) *outBroken = morkBool_kTrue;
if (outPos) *outPos = 0;
}
return outCount;
}
// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789