Files
raphael e2348db082 I1: SML parser — inverse of to_sml()
Adds parse_sml(text) -> Item / try_parse_sml(text) -> optional<Item>
in secs2/sml.hpp.  Round-trips with the existing to_sml() emitter for
every Item shape the codec produces: lists with nesting, ASCII / JIS8,
Binary (decimal and 0xHH literals), Boolean (T/F or 1/0, scalar and
multi-element), U1-U8 / I1-I8 / F4 / F8 vectors, and the optional
`[n]` count syntax (accepted but not enforced).

The parser is whitespace-insensitive outside quoted strings and uses
a small Cursor type for read_word / read_quoted / skip_ws.  Numeric
literals go through strtoul/strtoll/strtod so SML can carry hex,
octal, and decimal interchangeably (the emitter writes hex for Binary
and decimal everywhere else).

11 test cases cover the full round-trip surface, the whitespace
invariant, unknown-tag rejection, the try_parse error-swallowing
variant, and the optional `[n]` count.

secsgem-py has secs/sml.py for the same purpose; this brings the C++
port to parity on the tooling side.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-08 03:56:13 +02:00

33 lines
1.1 KiB
C++

#pragma once
#include <optional>
#include <stdexcept>
#include <string>
#include "secsgem/secs2/item.hpp"
// SML (SECS Message Language) parser — the inverse of `to_sml()` in
// codec.cpp. Accepts the same human-readable form emitted by
// Item::sml() / Message::sml(): each item is `<TAG [count]? values >`
// with nesting for lists. Strings are quoted; numeric literals are
// whitespace-separated; binary bytes use `0x` prefix or decimal.
//
// The grammar is loose but unambiguous. Whitespace (incl. newlines)
// is insignificant outside of quoted strings. The optional `[n]`
// count is accepted but not enforced — actual element counts come
// from the values that follow. Boolean accepts T/F as well as 1/0.
namespace secsgem::secs2 {
class SmlError : public std::runtime_error {
public:
using std::runtime_error::runtime_error;
};
// Parse a single Item. Throws SmlError on a malformed input.
Item parse_sml(const std::string& text);
// Try-style variant that returns nullopt instead of throwing.
std::optional<Item> try_parse_sml(const std::string& text);
} // namespace secsgem::secs2