summaryrefslogtreecommitdiff
path: root/db/mork/src/morkParser.h
blob: 9ddfd7731d259c917575ef95eaae615dbb1c4e37 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
/* -*- 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 _MORKPARSER_
#define _MORKPARSER_ 1

#ifndef _MORK_
#include "mork.h"
#endif

#ifndef _MORKBLOB_
#include "morkBlob.h"
#endif

#ifndef _MORKSINK_
#include "morkSink.h"
#endif

#ifndef _MORKYARN_
#include "morkYarn.h"
#endif

#ifndef _MORKCELL_
#include "morkCell.h"
#endif

//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
 
/*=============================================================================
 * morkPlace: stream byte position and stream line count
 */

class morkPlace {
public:
  mork_pos   mPlace_Pos;   // byte offset in an input stream
  mork_line  mPlace_Line;  // line count in an input stream
  
  void ClearPlace()
  {
    mPlace_Pos = 0; mPlace_Line = 0;
  }

  void SetPlace(mork_pos inPos, mork_line inLine)
  {
    mPlace_Pos = inPos; mPlace_Line = inLine;
  }

  morkPlace() { mPlace_Pos = 0; mPlace_Line = 0; }
  
  morkPlace(mork_pos inPos, mork_line inLine)
  { mPlace_Pos = inPos; mPlace_Line = inLine; }
  
  morkPlace(const morkPlace& inPlace)
  : mPlace_Pos(inPlace.mPlace_Pos), mPlace_Line(inPlace.mPlace_Line) { }
};

/*=============================================================================
 * morkGlitch: stream place and error comment describing a parsing error
 */

class morkGlitch {
public:
  morkPlace    mGlitch_Place;   // place in stream where problem happened
  const char*  mGlitch_Comment; // null-terminated ASCII C string

  morkGlitch() { mGlitch_Comment = 0; }
  
  morkGlitch(const morkPlace& inPlace, const char* inComment)
  : mGlitch_Place(inPlace), mGlitch_Comment(inComment) { }
};

/*=============================================================================
 * morkMid: all possible ways needed to express an alias ID in Mork syntax
 */

/*| morkMid: an abstraction of all the variations we might need to support
**| in order to present an ID through the parser interface most cheaply and
**| with minimum transformation away from the original text format.
**|
**|| An ID can have one of four forms:
**| 1) idHex            (mMid_Oid.mOid_Id <- idHex)
**| 2) idHex:^scopeHex  (mMid_Oid.mOid_Id <- idHex, mOid_Scope <- scopeHex)
**| 3) idHex:scopeName  (mMid_Oid.mOid_Id <- idHex, mMid_Buf <- scopeName)
**| 4) columnName       (mMid_Buf <- columnName, for columns in cells only)
**|
**|| Typically, mMid_Oid.mOid_Id will hold a nonzero integer value for
**| an ID, but we might have an optional scope specified by either an integer
**| in hex format, or a string name.  (Note that while the first ID can be
**| scoped variably, any integer ID for a scope is assumed always located in
**| the same scope, so the second ID need not be disambiguated.)
**|
**|| The only time mMid_Oid.mOid_Id is ever zero is when mMid_Buf alone
**| is nonzero, to indicate an explicit string instead of an alias appeared.
**| This case happens to make the representation of columns in cells somewhat
**| easier to represent, since columns can just appear as a string name; and
**| this unifies those interfaces with row and table APIs expecting IDs.
**|
**|| So when the parser passes an instance of morkMid to a subclass, the 
**| mMid_Oid.mOid_Id slot should usually be nonzero.  And the other two
**| slots, mMid_Oid.mOid_Scope and mMid_Buf, might both be zero, or at
**| most one of them will be nonzero to indicate an explicit scope; the
**| parser is responsible for ensuring at most one of these is nonzero.
|*/
class morkMid {
public:
  mdbOid          mMid_Oid;  // mOid_Scope is zero when not specified
  const morkBuf*  mMid_Buf;  // points to some specific buf subclass
  
  morkMid()
  { mMid_Oid.mOid_Scope = 0; mMid_Oid.mOid_Id = morkId_kMinusOne;
   mMid_Buf = 0; }
  
  void InitMidWithCoil(morkCoil* ioCoil)
  { mMid_Oid.mOid_Scope = 0; mMid_Oid.mOid_Id = morkId_kMinusOne;
   mMid_Buf = ioCoil; }
    
  void ClearMid()
  { mMid_Oid.mOid_Scope = 0; mMid_Oid.mOid_Id = morkId_kMinusOne;
   mMid_Buf = 0; }

  morkMid(const morkMid& other)
  : mMid_Oid(other.mMid_Oid), mMid_Buf(other.mMid_Buf) { }
  
  mork_bool HasNoId() const // ID is unspecified?
  { return ( mMid_Oid.mOid_Id == morkId_kMinusOne ); }
  
  mork_bool HasSomeId() const // ID is specified?
  { return ( mMid_Oid.mOid_Id != morkId_kMinusOne ); }
};

/*=============================================================================
 * morkSpan: start and end stream byte position and stream line count
 */

class morkSpan {
public:
  morkPlace   mSpan_Start;
  morkPlace   mSpan_End;

public: // methods
  
public: // inlines
  morkSpan() { } // use inline empty constructor for each place
  
  morkPlace* AsPlace() { return &mSpan_Start; }
  const morkPlace* AsConstPlace() const { return &mSpan_Start; }
  
  void SetSpan(mork_pos inFromPos, mork_line inFromLine,
    mork_pos inToPos, mork_line inToLine)
  {
    mSpan_Start.SetPlace(inFromPos, inFromLine);
    mSpan_End.SetPlace(inToPos,inToLine);
  }

  // setting end, useful to terminate a span using current port span end:
  void SetEndWithEnd(const morkSpan& inSpan) // end <- span.end
  { mSpan_End = inSpan.mSpan_End; }

  // setting start, useful to initiate a span using current port span end:
  void SetStartWithEnd(const morkSpan& inSpan) // start <- span.end
  { mSpan_Start = inSpan.mSpan_End; }
  
  void ClearSpan()
  {
    mSpan_Start.mPlace_Pos = 0; mSpan_Start.mPlace_Line = 0;
    mSpan_End.mPlace_Pos = 0; mSpan_End.mPlace_Line = 0;
  }

  morkSpan(mork_pos inFromPos, mork_line inFromLine,
    mork_pos inToPos, mork_line inToLine)
  : mSpan_Start(inFromPos, inFromLine), mSpan_End(inToPos, inToLine)
  {  /* empty implementation */ }
};

/*=============================================================================
 * morkParser: for parsing Mork text syntax
 */

#define morkParser_kMinGranularity 512 /* parse at least half 0.5K at once */
#define morkParser_kMaxGranularity (64 * 1024) /* parse at most 64 K at once */

#define morkDerived_kParser     /*i*/ 0x5073 /* ascii 'Ps' */
#define morkParser_kTag     /*i*/ 0x70417253 /* ascii 'pArS' */

// These are states for the simple parsing virtual machine.  Needless to say,
// these must be distinct, and preferrably in a contiguous integer range.
// Don't change these constants without looking at switch statements in code.
#define morkParser_kCellState      0 /* cell is tightest scope */
#define morkParser_kMetaState      1 /* meta is tightest scope */
#define morkParser_kRowState       2 /* row is tightest scope */
#define morkParser_kTableState     3 /* table is tightest scope */
#define morkParser_kDictState      4 /* dict is tightest scope */
#define morkParser_kPortState      5 /* port is tightest scope */

#define morkParser_kStartState     6 /* parsing has not yet begun */
#define morkParser_kDoneState      7 /* parsing is complete */
#define morkParser_kBrokenState    8 /* parsing is to broken to work */

class morkParser /*d*/ : public morkNode {

// public: // slots inherited from morkNode (meant to inform only)
  // nsIMdbHeap*       mNode_Heap;

  // mork_base      mNode_Base;     // must equal morkBase_kNode
  // mork_derived   mNode_Derived;  // depends on specific node subclass
  
  // mork_access    mNode_Access;   // kOpen, kClosing, kShut, or kDead
  // mork_usage     mNode_Usage;    // kHeap, kStack, kMember, kGlobal, kNone
  // mork_able      mNode_Mutable;  // can this node be modified?
  // mork_load      mNode_Load;     // is this node clean or dirty?
  
  // mork_uses      mNode_Uses;     // refcount for strong refs
  // mork_refs      mNode_Refs;     // refcount for strong refs + weak refs

// ````` ````` ````` `````   ````` ````` ````` `````  
protected: // protected morkParser members
  
  nsIMdbHeap*   mParser_Heap;   // refcounted heap used for allocation
  morkStream*   mParser_Stream; // refcounted input stream

  mork_u4       mParser_Tag; // must equal morkParser_kTag
  mork_count    mParser_MoreGranularity; // constructor inBytesPerParseSegment

  mork_u4       mParser_State; // state where parser should resume

  // after finding ends of group transactions, we can re-seek the start:
  mork_pos      mParser_GroupContentStartPos; // start of this group

  morkMid       mParser_TableMid; // table mid if inside a table
  morkMid       mParser_RowMid;   // row mid if inside a row
  morkMid       mParser_CellMid;  // cell mid if inside a row
  mork_gid      mParser_GroupId;  // group ID if inside a group

  mork_bool     mParser_InPort;  // called OnNewPort but not OnPortEnd?
  mork_bool     mParser_InDict;  // called OnNewDict but not OnDictEnd?
  mork_bool     mParser_InCell;  // called OnNewCell but not OnCellEnd?
  mork_bool     mParser_InMeta;  // called OnNewMeta but not OnMetaEnd?

  mork_bool     mParser_InPortRow;  // called OnNewPortRow but not OnPortRowEnd?
  mork_bool     mParser_InRow;    // called OnNewRow but not OnNewRowEnd?
  mork_bool     mParser_InTable;  // called OnNewMeta but not OnMetaEnd?
  mork_bool     mParser_InGroup;  // called OnNewGroup but not OnGroupEnd?

  mork_change   mParser_AtomChange;  // driven by mParser_Change 
  mork_change   mParser_CellChange;  // driven by mParser_Change 
  mork_change   mParser_RowChange;   // driven by mParser_Change 
  mork_change   mParser_TableChange; // driven by mParser_Change 

  mork_change   mParser_Change;     // driven by modifier in text 
  mork_bool     mParser_IsBroken;   // has the parse become broken?
  mork_bool     mParser_IsDone;     // has the parse finished?
  mork_bool     mParser_DoMore;     // mParser_MoreGranularity not exhausted? 

  morkMid       mParser_Mid;   // current alias being parsed
  // note that mParser_Mid.mMid_Buf points at mParser_ScopeCoil below:

  // blob coils allocated in mParser_Heap
  morkCoil     mParser_ScopeCoil;   // place to accumulate ID scope blobs
  morkCoil     mParser_ValueCoil;   // place to accumulate value blobs
  morkCoil     mParser_ColumnCoil;  // place to accumulate column blobs
  morkCoil     mParser_StringCoil;  // place to accumulate string blobs

  morkSpool    mParser_ScopeSpool;  // writes to mParser_ScopeCoil
  morkSpool    mParser_ValueSpool;  // writes to mParser_ValueCoil
  morkSpool    mParser_ColumnSpool; // writes to mParser_ColumnCoil
  morkSpool    mParser_StringSpool; // writes to mParser_StringCoil

  // yarns allocated in mParser_Heap
  morkYarn      mParser_MidYarn;   // place to receive from MidToYarn()

  // span showing current ongoing file position status:
  morkSpan      mParser_PortSpan; // span of current db port file

  // various spans denoting nested subspaces inside the file's port span:
  morkSpan      mParser_GroupSpan; // span of current transaction group
  morkSpan      mParser_DictSpan;
  morkSpan      mParser_AliasSpan;
  morkSpan      mParser_MetaSpan;
  morkSpan      mParser_TableSpan;
  morkSpan      mParser_RowSpan;
  morkSpan      mParser_CellSpan;
  morkSpan      mParser_ColumnSpan;
  morkSpan      mParser_SlotSpan;

private: // convenience inlines

  mork_pos HerePos() const
  { return mParser_PortSpan.mSpan_End.mPlace_Pos; }

  void SetHerePos(mork_pos inPos)
  { mParser_PortSpan.mSpan_End.mPlace_Pos = inPos; }

  void CountLineBreak()
  { ++mParser_PortSpan.mSpan_End.mPlace_Line; }

// { ===== begin morkNode interface =====
public: // morkNode virtual methods
  virtual void CloseMorkNode(morkEnv* ev) override; // CloseParser() only if open
  virtual ~morkParser(); // assert that CloseParser() executed earlier

public: // morkYarn construction & destruction
  morkParser(morkEnv* ev, const morkUsage& inUsage, nsIMdbHeap* ioHeap,
    morkStream* ioStream,  // the readonly stream for input bytes
    mdb_count inBytesPerParseSegment, // target for ParseMore()
    nsIMdbHeap* ioSlotHeap);
      
  void CloseParser(morkEnv* ev); // called by CloseMorkNode();

private: // copying is not allowed
  morkParser(const morkParser& other);
  morkParser& operator=(const morkParser& other);

public: // dynamic type identification
  mork_bool IsParser() const
  { return IsNode() && mNode_Derived == morkDerived_kParser; }
  
// } ===== end morkNode methods =====

public: // errors and warnings
  static void UnexpectedEofError(morkEnv* ev);
  static void EofInsteadOfHexError(morkEnv* ev);
  static void ExpectedEqualError(morkEnv* ev);
  static void ExpectedHexDigitError(morkEnv* ev, int c);
  static void NonParserTypeError(morkEnv* ev);
  static void UnexpectedByteInMetaWarning(morkEnv* ev);

public: // other type methods
  mork_bool GoodParserTag() const { return mParser_Tag == morkParser_kTag; }
  void NonGoodParserError(morkEnv* ev);
  void NonUsableParserError(morkEnv* ev);
  // call when IsNode() or GoodParserTag() is false
  
// ````` ````` ````` `````   ````` ````` ````` `````  
public: // in virtual morkParser methods, data flow subclass to parser

    virtual void MidToYarn(morkEnv* ev,
      const morkMid& inMid,  // typically an alias to concat with strings
      mdbYarn* outYarn) = 0;
    // The parser might ask that some aliases be turned into yarns, so they
    // can be concatenated into longer blobs under some circumstances.  This
    // is an alternative to using a long and complex callback for many parts
    // for a single cell value.
  
// ````` ````` ````` `````   ````` ````` ````` `````  
public: // out virtual morkParser methods, data flow parser to subclass

// The virtual methods below will be called in a pattern corresponding
// to the following grammar isomorphic to the Mork grammar.  There should
// be no exceptions, so subclasses can rely on seeing an appropriate "end"
// method whenever some "new" method has been seen earlier.  In the event
// that some error occurs that causes content to be flushed, or sudden early
// termination of a larger containing entity, we will always call a more
// enclosed "end" method before we call an "end" method with greater scope.

// Note the "mp" prefix stands for "Mork Parser":

// mp:Start     ::= OnNewPort mp:PortItem* OnPortEnd
// mp:PortItem  ::= mp:Content | mp:Group | OnPortGlitch
// mp:Group     ::= OnNewGroup mp:GroupItem* mp:GroupEnd
// mp:GroupItem ::= mp:Content | OnGroupGlitch
// mp:GroupEnd  ::= OnGroupCommitEnd | OnGroupAbortEnd
// mp:Content   ::= mp:PortRow | mp:Dict | mp:Table | mp:Row
// mp:PortRow   ::= OnNewPortRow mp:RowItem* OnPortRowEnd
// mp:Dict      ::= OnNewDict mp:DictItem* OnDictEnd
// mp:DictItem  ::= OnAlias | OnAliasGlitch | mp:Meta | OnDictGlitch
// mp:Table     ::= OnNewTable mp:TableItem* OnTableEnd
// mp:TableItem ::= mp:Row | mp:MetaTable | OnTableGlitch
// mp:MetaTable ::= OnNewMeta mp:MetaItem* mp:Row OnMetaEnd
// mp:Meta      ::= OnNewMeta mp:MetaItem* OnMetaEnd
// mp:MetaItem  ::= mp:Cell | OnMetaGlitch
// mp:Row       ::= OnMinusRow? OnNewRow mp:RowItem* OnRowEnd
// mp:RowItem   ::= mp:Cell | mp:Meta | OnRowGlitch
// mp:Cell      ::= OnMinusCell? OnNewCell mp:CellItem? OnCellEnd
// mp:CellItem  ::= mp:Slot | OnCellForm | OnCellGlitch
// mp:Slot      ::= OnValue | OnValueMid | OnRowMid | OnTableMid


  // Note that in interfaces below, mork_change parameters kAdd and kNil
  // both mean about the same thing by default.  Only kCut is interesting,
  // because this usually means to remove members instead of adding them.

  virtual void OnNewPort(morkEnv* ev, const morkPlace& inPlace) = 0;
  virtual void OnPortGlitch(morkEnv* ev, const morkGlitch& inGlitch) = 0;  
  virtual void OnPortEnd(morkEnv* ev, const morkSpan& inSpan) = 0;  

  virtual void OnNewGroup(morkEnv* ev, const morkPlace& inPlace, mork_gid inGid) = 0;
  virtual void OnGroupGlitch(morkEnv* ev, const morkGlitch& inGlitch) = 0;  
  virtual void OnGroupCommitEnd(morkEnv* ev, const morkSpan& inSpan) = 0;  
  virtual void OnGroupAbortEnd(morkEnv* ev, const morkSpan& inSpan) = 0;  

  virtual void OnNewPortRow(morkEnv* ev, const morkPlace& inPlace, 
    const morkMid& inMid, mork_change inChange) = 0;
  virtual void OnPortRowGlitch(morkEnv* ev, const morkGlitch& inGlitch) = 0;  
  virtual void OnPortRowEnd(morkEnv* ev, const morkSpan& inSpan) = 0;  

  virtual void OnNewTable(morkEnv* ev, const morkPlace& inPlace,
    const morkMid& inMid, mork_bool inCutAllRows) = 0;
  virtual void OnTableGlitch(morkEnv* ev, const morkGlitch& inGlitch) = 0;
  virtual void OnTableEnd(morkEnv* ev, const morkSpan& inSpan) = 0;
    
  virtual void OnNewMeta(morkEnv* ev, const morkPlace& inPlace) = 0;
  virtual void OnMetaGlitch(morkEnv* ev, const morkGlitch& inGlitch) = 0;
  virtual void OnMetaEnd(morkEnv* ev, const morkSpan& inSpan) = 0;

  virtual void OnMinusRow(morkEnv* ev) = 0;
  virtual void OnNewRow(morkEnv* ev, const morkPlace& inPlace, 
    const morkMid& inMid, mork_bool inCutAllCols) = 0;
  virtual void OnRowPos(morkEnv* ev, mork_pos inRowPos) = 0;  
  virtual void OnRowGlitch(morkEnv* ev, const morkGlitch& inGlitch) = 0;  
  virtual void OnRowEnd(morkEnv* ev, const morkSpan& inSpan) = 0;  

  virtual void OnNewDict(morkEnv* ev, const morkPlace& inPlace) = 0;
  virtual void OnDictGlitch(morkEnv* ev, const morkGlitch& inGlitch) = 0;  
  virtual void OnDictEnd(morkEnv* ev, const morkSpan& inSpan) = 0;  

  virtual void OnAlias(morkEnv* ev, const morkSpan& inSpan,
    const morkMid& inMid) = 0;

  virtual void OnAliasGlitch(morkEnv* ev, const morkGlitch& inGlitch) = 0;

  virtual void OnMinusCell(morkEnv* ev) = 0;
  virtual void OnNewCell(morkEnv* ev, const morkPlace& inPlace,
    const morkMid* inMid, const morkBuf* inBuf) = 0;
  // Exactly one of inMid and inBuf is nil, and the other is non-nil.
  // When hex ID syntax is used for a column, then inMid is not nil, and
  // when a naked string names a column, then inBuf is not nil.
    
  virtual void OnCellGlitch(morkEnv* ev, const morkGlitch& inGlitch) = 0;
  virtual void OnCellForm(morkEnv* ev, mork_cscode inCharsetFormat) = 0;
  virtual void OnCellEnd(morkEnv* ev, const morkSpan& inSpan) = 0;
    
  virtual void OnValue(morkEnv* ev, const morkSpan& inSpan,
    const morkBuf& inBuf) = 0;

  virtual void OnValueMid(morkEnv* ev, const morkSpan& inSpan,
    const morkMid& inMid) = 0;

  virtual void OnRowMid(morkEnv* ev, const morkSpan& inSpan,
    const morkMid& inMid) = 0;

  virtual void OnTableMid(morkEnv* ev, const morkSpan& inSpan,
    const morkMid& inMid) = 0;
  
// ````` ````` ````` `````   ````` ````` ````` `````  
protected: // protected parser helper methods

  void ParseChunk(morkEnv* ev); // find parse continuation and resume

  void StartParse(morkEnv* ev); // prepare for parsing
  void StopParse(morkEnv* ev); // terminate parsing & call needed methods

  int NextChar(morkEnv* ev); // next non-white content

  void OnCellState(morkEnv* ev);
  void OnMetaState(morkEnv* ev);
  void OnRowState(morkEnv* ev);
  void OnTableState(morkEnv* ev);
  void OnDictState(morkEnv* ev);
  void OnPortState(morkEnv* ev);
  void OnStartState(morkEnv* ev);
  
  void ReadCell(morkEnv* ev);
  void ReadRow(morkEnv* ev, int c);
  void ReadRowPos(morkEnv* ev);
  void ReadTable(morkEnv* ev);
  void ReadTableMeta(morkEnv* ev);
  void ReadDict(morkEnv* ev);
  mork_bool ReadContent(morkEnv* ev, mork_bool inInsideGroup);
  void ReadGroup(morkEnv* ev);
  mork_bool ReadEndGroupId(morkEnv* ev);
  mork_bool ReadAt(morkEnv* ev, mork_bool inInsideGroup);
  mork_bool FindGroupEnd(morkEnv* ev);
  void ReadMeta(morkEnv* ev, int inEndMeta);
  void ReadAlias(morkEnv* ev);
  mork_id ReadHex(morkEnv* ev, int* outNextChar);
  morkBuf* ReadValue(morkEnv* ev);
  morkBuf* ReadName(morkEnv* ev, int c);
  mork_bool ReadMid(morkEnv* ev, morkMid* outMid);
  void ReadDictForm(morkEnv *ev);
  void ReadCellForm(morkEnv *ev, int c);
  
  mork_bool MatchPattern(morkEnv* ev, const char* inPattern);
  
  void EndSpanOnThisByte(morkEnv* ev, morkSpan* ioSpan);
  void EndSpanOnLastByte(morkEnv* ev, morkSpan* ioSpan);
  void StartSpanOnLastByte(morkEnv* ev, morkSpan* ioSpan);
  
  void StartSpanOnThisByte(morkEnv* ev, morkSpan* ioSpan);
  
  
  // void EndSpanOnThisByte(morkEnv* ev, morkSpan* ioSpan)
  // { MORK_USED_2(ev,ioSpan); }
  
  // void EndSpanOnLastByte(morkEnv* ev, morkSpan* ioSpan)
  // { MORK_USED_2(ev,ioSpan); }
  
  // void StartSpanOnLastByte(morkEnv* ev, morkSpan* ioSpan)
  // { MORK_USED_2(ev,ioSpan); }
  
  // void StartSpanOnThisByte(morkEnv* ev, morkSpan* ioSpan)
  // { MORK_USED_2(ev,ioSpan); }
  
  int eat_line_break(morkEnv* ev, int inLast);
  int eat_line_continue(morkEnv* ev); // last char was '\\'
  int eat_comment(morkEnv* ev); // last char was '/'
  
// ````` ````` ````` `````   ````` ````` ````` `````  
public: // public non-poly morkParser methods
    
  mdb_count 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?
  );
  
  
public: // typesafe refcounting inlines calling inherited morkNode methods
  static void SlotWeakParser(morkParser* me,
    morkEnv* ev, morkParser** ioSlot)
  { morkNode::SlotWeakNode((morkNode*) me, ev, (morkNode**) ioSlot); }
  
  static void SlotStrongParser(morkParser* me,
    morkEnv* ev, morkParser** ioSlot)
  { morkNode::SlotStrongNode((morkNode*) me, ev, (morkNode**) ioSlot); }
};

//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789

#endif /* _MORKPARSER_ */