Defined in header <cparse/Cursor.hh>

class Cursor;

Cursor is a read-only position in an std::istream. On top of what the standard stream cursor gives you, it adds three things:

  1. Line and column tracking that updates automatically on every read.
  2. A save/restore mechanism for cheap backtracking without seeking manually.
  3. Safe variants of get and peek that return an EOF sentinel '\032' (^Z) instead of throwing when the stream cannot be read.

Grammars almost never construct a Cursor directly — Parser::parse() does that for the outermost file, and Parser::pushCursor() does it for nested inputs. But grammars call cursor methods on every character consumed, so the interface matters.

Member types

Member typeDefinition
stream_typestd::istream

Member functions

(constructor)constructs the cursor
(destructor)destroys the cursor (virtual)
operator=assigns from another cursor
operator==compares two cursors for equality

Position management

rewindseeks to start of stream and resets line/column
storerecords the current position for later restore
restoreseeks back to a previously stored position
safeRestorerestore with exception-mask handling for post-EOF recovery
isValidchecks whether the stream is set and not in eof/bad state

Reading

getconsumes and returns the next character; throws at EOF
safeGetas get, but returns EOF sentinel instead of throwing
peekinspects the next character without consuming; throws at EOF
safePeekas peek, but returns EOF sentinel instead of throwing

Location accessors

getLocationreturns the shared filename pointer
getFilenamereturns the filename by reference
getLineNumberreturns the current one-based line number
getColumnNumberreturns the current one-based column number

Notes

The stream is held via std::shared_ptr. Copies of a Cursor share the same underlying stream, so store() / restore() cannot "undo" a get() on the stream itself — they only reset the recorded position and seek the underlying stream back. This is fine for files but may be surprising for non-seekable streams.

Move construction and move assignment are deleted. The rationale is that Parser::storeCursor() returns a Cursor const& (aliasing the parser's own cursor) and callers who need an independent snapshot must make an explicit copy — which move-elision would silently break.

The destructor is virtual. The class is not final, though it is not currently subclassed inside cparse.

Example

#include <cparse/Cursor.hh>
#include <iostream>
#include <sstream>

int main()
{
    auto ss = std::make_shared<std::istringstream>("hello\nworld");
    fedem::parser::Cursor c("<memory>", ss);

    while (c.isValid())
    {
        auto ch = c.safeGet();
        if (ch == '\032') break;
        std::cout << ch;
    }
    std::cout << "\nreached line " << c.getLineNumber()
              << ", column "       << c.getColumnNumber() << '\n';
}

Output:

hello
world
reached line 2, column 5

See also

  • Parser — the abstract driver that owns and drives Cursor instances.
  • Getting Started — how a grammar consumes a Cursor.