IRSOL
C++ code implementing socket server for interacting with Baumer camera.
irsol::protocol::Parser Class Reference

Parses raw input strings into structured InMessage instances. More...

#include <parser.hpp>

Static Public Member Functions

static std::optional< InMessageparse (const std::string &line)
 Attempts to parse a single protocol input line into a structured InMessage.
 

Static Private Member Functions

static internal::ParserResult< AssignmentparseAssignment (const std::string &line)
 Parses an assignment message from a protocol line.
 
static internal::ParserResult< InquiryparseInquiry (const std::string &line)
 Parses an inquiry message from a protocol line.
 
static internal::ParserResult< CommandparseCommand (const std::string &line)
 Parses a command message from a protocol line.
 
static irsol::types::protocol_value_t parseValue (const std::string &valueString)
 Attempts to parse a raw value string into a typed protocol value.
 

Detailed Description

Parses raw input strings into structured InMessage instances.

The Parser provides static methods to convert protocol input lines (e.g., x=42, reset, x?) into well-formed irsol::protocol::InMessage variants. Parsing is designed to be robust and safe, returning an empty result on failure.

Definition at line 31 of file parser.hpp.

Member Function Documentation

◆ parse()

std::optional< InMessage > irsol::protocol::Parser::parse ( const std::string &  line)
static

Attempts to parse a single protocol input line into a structured InMessage.

This method sequentially tries to interpret the input string as one of the supported message types:

  • Assignment (e.g., "foo=42" or "temp[1]=36.5"): Key-value bindings with optional indexed identifiers.
  • Inquiry (e.g., "foo?" or "temp[2]?"): Used to query the current value of an identifier.
  • Command (e.g., "reset" or "shutdown"): Control messages without arguments.

The function internally trims whitespace, applies regular expressions to match expected message formats, and uses helper functions to convert the raw string into a strongly-typed variant.

Parsing proceeds in order of decreasing specificity:

  1. parseAssignment() is attempted first. If it fails, the reason is recorded.
  2. parseInquiry() is attempted next. If it fails, the reason is recorded.
  3. parseCommand() is attempted last.

If none of the parsers succeed, a warning is logged with details from each parsing attempt, and the method returns std::nullopt.

Parameters
lineA raw protocol line (e.g., from a client or script), which may contain leading/trailing whitespace.
Returns
An optional containing the parsed InMessage variant if successful, or std::nullopt on failure.
See also
parseAssignment
parseInquiry
parseCommand

Definition at line 11 of file parser.cpp.

12{
13 IRSOL_LOG_TRACE("Parsing string '{}' for message", line);
14 std::string s = utils::trim(line);
15 IRSOL_LOG_TRACE("Trimmed result: '{}'", s);
16 std::vector<std::string> errorMessages;
17 errorMessages.reserve(3);
18 if(auto asg = parseAssignment(s)) {
20 "String '{}' parsed as assignment message: '{}'", line, asg.getMessage().toString());
21 return asg.getMessage();
22 } else {
23 errorMessages.push_back(asg.getError());
24 }
25 if(auto inq = parseInquiry(s)) {
26 IRSOL_LOG_TRACE("String '{}' parsed as inquiry message: {}", line, inq.getMessage().toString());
27 return inq.getMessage();
28 } else {
29 errorMessages.push_back(inq.getError());
30 }
31 if(auto cmd = parseCommand(s)) {
32 IRSOL_LOG_TRACE("String '{}' parsed as command message: {}", line, cmd.getMessage().toString());
33 return cmd.getMessage();
34 } else {
35 errorMessages.push_back(cmd.getError());
36 }
37
38 std::string fullErrorMessage = "";
39 for(const auto& errorMessage : errorMessages) {
40 fullErrorMessage += errorMessage + "; ";
41 }
43 "String '{}' could not be parsed as any known message type. {}", line, fullErrorMessage);
44 return std::nullopt;
45}
static internal::ParserResult< Inquiry > parseInquiry(const std::string &line)
Parses an inquiry message from a protocol line.
Definition parser.cpp:81
static internal::ParserResult< Assignment > parseAssignment(const std::string &line)
Parses an assignment message from a protocol line.
Definition parser.cpp:48
static internal::ParserResult< Command > parseCommand(const std::string &line)
Parses a command message from a protocol line.
Definition parser.cpp:113
#define IRSOL_LOG_WARN(...)
Logs a warning-level message using the default logger.
Definition logging.hpp:93
#define IRSOL_LOG_TRACE(...)
Logs a trace-level message using the default logger.
Definition logging.hpp:90
std::string trim(const std::string &s)
Remove leading and trailing whitespace from a string.
Definition utils.hpp:122
auto asg

◆ parseAssignment()

internal::ParserResult< Assignment > irsol::protocol::Parser::parseAssignment ( const std::string &  line)
staticprivate

Parses an assignment message from a protocol line.

Matches lines of the form:

identifier=value
auto value

where:

  • identifier must begin with a letter and contain alphanumeric characters and underscores.
  • Array-style indexing is supported, e.g., array[0], arr[1][2].
  • value is interpreted as an integer, double, or string, depending on its format.

Valid examples:

  • "x=42"
  • "temperature[1]=36.5"
  • ‘"name='Alice&rsquo;"`
  • `"meta={some description}"`

If the regex matches but value parsing fails, an error string is returned. If the format does not match, a descriptive parsing error is returned.

Parameters
lineThe raw line to parse.
Returns
A ParserResult containing a valid Assignment or an error message.

Definition at line 48 of file parser.cpp.

49{
50 // An assignment line looks like:
51 // <identifier>=<value>
52 // where:
53 // <identifier> is a string of alphanumeric characters and underscores (no spaces allowed), with
54 // optional array-indexing <value> is a string that is dynamically interpreted as an integer,
55 // double, or string The '=' is mandatory.
56
57 // Example valid assignment lines:
58 // foo=32
59 // bar=53.6
60 // qux_123=0.432
61 // _underscore=43
62 // array_like[1]=hello
63 // nested_array_like[1][2]=0 -> TODO: multidimensional has commas for indices and not multiple
64 // brackets. single_quote='single quote' double_quote="double quote" braces={sting value}
65 static const std::regex re(R"(^([a-zA-Z]+[a-zA-Z0-9_]*(?:\[\d+\])*)=(.+)$)");
66 std::smatch m;
67 std::string errorMessage;
68 if(std::regex_match(line, m, re)) {
69 try {
70 return {Assignment{utils::trim(m[1]), parseValue(utils::trim(m[2]))}};
71 } catch(const std::invalid_argument& e) {
72 errorMessage = e.what();
73 }
74 } else {
75 errorMessage = "Regex pattern for Assignment did not match";
76 }
77 return {std::move(errorMessage)};
78}
static irsol::types::protocol_value_t parseValue(const std::string &valueString)
Attempts to parse a raw value string into a typed protocol value.
Definition parser.cpp:142
irsol::protocol::Assignment m(identifier, value)

◆ parseCommand()

internal::ParserResult< Command > irsol::protocol::Parser::parseCommand ( const std::string &  line)
staticprivate

Parses a command message from a protocol line.

Matches lines consisting of a single bare identifier:

identifier

where:

  • identifier must start with a letter and contain only alphanumeric characters and underscores.
  • No parameters or arguments are allowed.

Valid examples:

  • "reset"
  • "shutdown"
  • "ping"

Returns a Command object on success, or an error message on failure.

Parameters
lineThe raw line to parse.
Returns
A ParserResult containing a valid Command or an error message.

Definition at line 113 of file parser.cpp.

114{
115 // A command line looks like:
116 // <identifier>
117 // where:
118 // <identifier> is a string of alphanumeric characters and underscores (no spaces allowed)
119
120 // Example valid command lines:
121 // foo
122 // bar
123 // qux_123
124 // _underscore
125
126 static const std::regex re(R"(^([a-zA-Z]+[a-zA-Z0-9_]*)$)");
127 std::smatch m;
128 std::string errorMessage;
129 if(std::regex_match(line, m, re)) {
130 try {
131 return {Command{utils::trim(m[1])}};
132 } catch(const std::invalid_argument& e) {
133 errorMessage = e.what();
134 }
135 } else {
136 errorMessage = "Regex pattern for Command did not match";
137 }
138 return {std::move(errorMessage)};
139}

◆ parseInquiry()

internal::ParserResult< Inquiry > irsol::protocol::Parser::parseInquiry ( const std::string &  line)
staticprivate

Parses an inquiry message from a protocol line.

Matches lines of the form:

identifier?

where:

  • identifier must begin with a letter and contain alphanumeric characters and underscores.
  • Array-style indexing is supported (e.g., array[0]).
  • Whitespace is trimmed before matching.

Valid examples:

  • "x?"
  • "temperature[1]?"
  • "config_setting?"

Returns a valid Inquiry message on success, or a detailed error message on failure.

Parameters
lineThe raw line to parse.
Returns
A ParserResult containing a valid Inquiry or an error message.

Definition at line 81 of file parser.cpp.

82{
83 // An inquiry line looks like:
84 // <identifier>?
85 // where:
86 // <identifier> is a string of alphanumeric characters and underscores (no spaces allowed), with
87 // optional array-indexing The '?' is mandatory.
88
89 // Example valid inquiry lines:
90 // foo?
91 // bar?
92 // qux_123?
93 // _underscore?
94 // array_like[1]?
95 // nested_array_like[1][2]?
96
97 static const std::regex re(R"(^([a-zA-Z]+[a-zA-Z0-9_]*(?:\[\d+\])*)\?$)");
98 std::smatch m;
99 std::string errorMessage;
100 if(std::regex_match(line, m, re)) {
101 try {
102 return {Inquiry{utils::trim(m[1])}};
103 } catch(const std::invalid_argument& e) {
104 errorMessage = e.what();
105 }
106 } else {
107 errorMessage = "Regex pattern for Inquiry did not match";
108 }
109 return {std::move(errorMessage)};
110}

◆ parseValue()

irsol::types::protocol_value_t irsol::protocol::Parser::parseValue ( const std::string &  valueString)
staticprivate

Attempts to parse a raw value string into a typed protocol value.

The parser uses the following heuristics, in order:

  1. If the string can be converted to a double, and contains . or scientific notation (e/E), it is treated as a double.
  2. If the value is numeric and within int range, it is converted to int.
  3. If the value is quoted with ‘’,"<tt>, or wrapped in</tt>{}<tt>, the delimiters are stripped and the result is returned as a</tt>std::string`. 4. Otherwise, the raw string is returned as-is. Examples: - <tt>"42"</tt> → <tt>int</tt> - <tt>"3.14"</tt> → <tt>double</tt> - <tt>"1e-3"</tt> → <tt>double</tt> - &lsquo;"'hello’"`, `'"hello"'`, `"{hello}"` → `"hello"` - `"raw_string"` → `"raw_string"`
Parameters
valueStringThe string representation of the value (right-hand side of an assignment).
Returns
A irsol::types::protocol_value_t holding the parsed value.

Definition at line 142 of file parser.cpp.

143{
144 // Try double first (more general)
145 try {
146 double d = utils::fromString<double>(valStr);
147
148 // Heuristic: if the string contains a '.' or 'e'/'E', treat it as double
149 // This allows to parse a value such as '5.0' as a double, not an int.
150 if(
151 valStr.find('.') != std::string::npos || valStr.find('e') != std::string::npos ||
152 valStr.find('E') != std::string::npos) {
153 return d;
154 }
155
156 // Otherwise, treat as int if within range
157 if(d >= std::numeric_limits<int>::min() && d <= std::numeric_limits<int>::max()) {
158 return static_cast<int>(d);
159 }
160
161 return d;
162 } catch(...) {
163 // Fall through
164 }
165
166 // Else, string (strip quotes/braces if needed)
167 if(
168 !valStr.empty() && ((valStr.front() == '"' && valStr.back() == '"') ||
169 (valStr.front() == '\'' && valStr.back() == '\'') ||
170 (valStr.front() == '{' && valStr.back() == '}'))) {
171 return valStr.substr(1, valStr.size() - 2);
172 }
173
174 return valStr;
175}

The documentation for this class was generated from the following files: