The answers below come out of real grammar-writing sessions with cparse. If you hit a puzzle that is not here, the Reference Manual usually contains the detail you need — the classes are small and every member has its own page.

Why does my program throw std::ios_base::failure from Cursor::get()?

That is by design. The cursor's stream has its exception mask set to throw on failbit | eofbit | badbit, and end-of-input surfaces as ios_base::failure. Parser::parse catches this in its Phase 5 handler and translates it to a return value.

If it is escaping to your main function, you are probably calling Cursor::get() from outside a parse() call, or you have a grammar helper that catches the exception and rethrows it. Use Cursor::safeGet() inside your own loops — it returns the sentinel '\032' instead of throwing.

safePeek() keeps returning the sentinel even after I add more characters. Why?

You probably have a stale EOF bit on the stream. Cursor::safePeek explicitly clears the eof bit before peeking (see the safePeek reference), so on the cursor's own operations this should not happen. It usually means you are looking at a stream that was manipulated outside the cursor (a seekg() in your own code, for example, or a stream you passed in that another writer touched).

If you must manipulate the stream directly, cursor.safePeek() after a manual stream.clear() should recover.

Why is my error's line reported as zero?

Two possibilities:

  1. You called recordError with explicit line = 0 and col = 0. Zero is a sentinel meaning "use the active cursor's line/column". If the parser had no active cursor at that moment (you called recordError before parse() started, or outside a parse() frame), zero survives.
  2. The error occurred before the first character was consumed, so the cursor genuinely reports line 0 / column 0.

parse() returns true but errors() is non-empty. Bug?

No. Non-fatal errors (Kind::Warning, and Kind::Syntax diagnostics recorded by a grammar that then recovered and completed) do not affect the return value. If you need "no diagnostics at all", combine the return value with hasErrors():

bool const ok = p.parse(argv[1]);
if (ok && !p.hasErrors())
    std::cout << "clean parse\n";

Do I need to call enterScope/leaveScope?

Only if your grammar has nested constructs (braces, brackets, begin/end) and you want a truncated file to be detected. Grammars without nesting keep the depth at zero forever and behave exactly as a grammar that predates the mechanism — no change of behaviour, no cost.

If your grammar does have nesting: yes, please. Without the calls, a file cut short at a member boundary parses as valid with the missing half silently defaulted. This has bitten real users on FSON files interrupted by a full disk.

Why can I not wrap enterScope in an RAII guard?

The depth must stay non-zero while the EOF exception unwinds, because that unwinding is the condition being detected. A guard that decremented in its destructor during unwinding would erase the evidence: by the time parse() catches the EOF and reads the depth, it would be back to zero, and a truncated file would look like a clean EOF.

Parser::enterScope has the full rationale in its Notes section — it is worth reading if the argument does not click immediately.

Can I have multiple Parser instances at once?

Yes. Each Parser owns its own cursor stack and error collector; they do not share state. What is deleted is Parser's own copy / move constructors — you cannot copy or move an existing instance, but you can construct as many as you like.

If you are trying to parse the same input in two different ways concurrently, each pass gets its own Parser and its own std::ifstream (or shared istringstream for in-memory input).

Cursor copies share the stream. Is that a problem?

Rarely. Cursor holds an std::shared_ptr to the underlying stream, so copies share it. Both copies see the same tellg() position after either one advances the stream.

The idiomatic backtracking pattern relies on this being sane: Cursor snapshot = getCursor(); captures the position into the Cursor object (line, column, tellg() result stored in store()), not into the stream. Later restoreCursor(snapshot) copies the position back and calls restore(), seeking the shared stream to the recorded position. Both cursors are then back at the saved point.

Cursor copies share the stream. Is that a problem?

Same question, different bite — worth repeating: file streams opened by Cursor's single-argument constructor are closed when the last Cursor referring to them is destroyed. If you keep a Cursor around after the Parser is gone, the file stays open. If you have thousands of long-lived cursors, you can run out of file descriptors. In practice grammars finish and everything destructs; this is only a concern in unusual driver programs.

Why does Generator::convertLowerCase mishandle Unicode?

It is byte-oriented and uses std::tolower, which is locale-free and only handles ASCII. Multi-byte UTF-8 sequences pass through unchanged. Turkish İ does not become i; German ß does not become ss.

For emitted code — file names, C++ identifiers, header guards — ASCII-only behaviour is exactly what you want, so cparse commits to it. If you need locale-aware case folding for user-visible strings, do that in your subclass with your own routine before handing the string to Generator.

generateHeaderGuard produced a guard with a leading digit. Now the header does not compile.

generateHeaderGuard upper-cases and sanitises but does not reject leading digits — 123.hh under an empty namespace becomes 123_HH, which is not a valid C++ identifier.

Two options:

  • Ensure your header names begin with a letter before you call it.
  • Wrap the call in your subclass and prepend a fixed prefix: cpp std::string myGuard(std::string const& h) const noexcept { auto raw = generateHeaderGuard(h); if (!raw.empty() && std::isdigit(static_cast<unsigned char>(raw[0]))) raw.insert(0, "H_"); return raw; }

encodeCppString emitted \000A for two bytes but I wanted \0A. Why?

encodeCppString always emits three-digit octal for non-printable bytes below \x20 (and for the null byte specifically). The reason: \0A in C++ means the character with octal value 0 followed by the letter A — indistinguishable from what you wrote by hand and correct on its own, but ambiguous if the next byte were a digit. \000A is unambiguous regardless of what follows. It is a defensive choice.

Can I use cparse for a binary format?

Yes, but you lose some of the value. Cursor is designed for text — it tracks line and column, and the safe/throwing variants assume a text-like end-of-input path. For binary you can still use safeGet() in a loop and process bytes, but the whitespace, comment, extractToken and enterScope helpers do not really apply, and the line/column tracking is spurious.

A binary format is often better served by writing directly against std::istream without the cparse wrapper.

The pushed cursor never seems to get popped.

Two common causes:

  • Your grammar never runs the pushed cursor to end-of-input. The return stack pops only when the pushed cursor's EOF exception unwinds through parse() Phase 6. Consuming a subset and returning normally leaves the cursor active. If you want to pop manually, that is not supported by design — restructure the code so the pushed cursor is exhausted (or its EOF is triggered by a grammar that reads until EOF).
  • The grammar caught the EOF exception itself and did not rethrow. Cursor::get() throws on EOF and parse() catches it; if your intermediate code swallows the exception, the pop never happens.

Where do I read the code?

The library is small — 1200 lines total across 4 headers and 4 implementation files under src/cparse/. If any behaviour is ambiguous in the reference, reading the source is often faster than opening a bug tracker. The header comments in Parser.hh and the exception-handling logic in Parser::parse() are the densest parts.

See also