|
mstring
1.3.1
Generates C++ message functions and classes from localized message definitions
|
A message file defines messages: texts with parameters, in one or more languages. mstring turns it into a C++ header and source with, per message, the functions and classes the settings ask for.
The grammar, one railroad diagram per rule, is in [docs/grammar/](grammar) (NNNNN-<rule>.rrd sources, .svg drawings, render.py to redraw them) and on mstring.fedem.eu under Language.
The file is line oriented. Outside a message only $DIRECTIVE lines, blank lines and # comments may appear. Directive and option names are case-insensitive ($MESSAGE, $message, $Message).
name is a C++ identifier and must be unique in the module. A message ends at the next $DIRECTIVE (or the end of the file). A message without any text gets a warning.
The settings directives below are sticky: they apply to every message that follows. To choose the artefacts of one message only, list them after a colon — exactly these are generated, named by the current settings:
The artefacts are STREAMABLE, STRING, ERROR, EXCEPTION, THROW and SYSLOG.
The type is the rest of the line. The names stream, loc, option_parameter, streamLocale, originalFormat, internal_locale_name and names starting with member_ or internal_ are reserved for the generated code.
A generated class (streamable, exception) stores a parameter as std::decay_t< type >, so an object built from a std::string const & never refers to a temporary.
'single quoted' — no macros ({$x} is literal), no newline at the end;"double quoted" — macros, no newline at the end;|bar text (to the end of the line) — macros, and a newline at the end, unless the line ends with a backslash.The lines of one language are concatenated. Blanks before the opening quote or bar are ignored; inside the text they are kept.
Escapes, in every form:
\n \t \r \a \b \f \v — control characters;\{ — a literal {: \{$x} is not a macro;\303), or \x and 1–2 hex digits (\xC3) — that byte;C:\temp stays C:\temp.A |bar line that ends with \ is joined to the next line without a newline. A quoted string must close on its line.
expression is any C++ expression, usually a parameter; it is written to the stream as stream << ( expression ). Braces inside it may nest ({$ std::string{ "x" } }); a ; or an unbalanced } can be escaped as \; / \}.
A format applies to this value only — the stream's formatting (flags, precision, width, fill, locale) is restored afterwards:
std::setw(4), std::hex, std::fixed, … — a manipulator: stream << std::setw(4);width(4), precision(2), fill('0'), setf(...), unsetf(...), flags(...), imbue(...), or anything starting with . — a member call: stream.precision(2);Manipulators with arguments need $INCLUDE <iomanip>.
A [language] header switches the language of the text lines that follow, until the next header. The generated code picks the text by the locale name of the stream it writes to:
_, . or @: [tr] and [tr_TR] both match tr_TR.UTF-8, [C] matches C.UTF-8;tr_TR before tr);$LANGUAGE, or else its first text.The text bytes are written byte for byte (as octal escapes), so the source file's charset is the charset of the texts.
$CHARSET name and a charset in the header ([de ISO-8859-1]) are deprecated: they only label the text with a comment in the generated code, and give a warning.
After parsing, mstring warns about
Per message the source gets up to two internal text functions: one that writes the text to a std::ostream (used by the streamable class and the error function) and one that appends it to a std::string without a stream (used by the string and syslog functions and the exception's What()). The latter appends literals directly, string values as they are and integers with std::to_chars when the locale is "C"; everything else — and every macro with formats — goes through a std::ostringstream imbued with the locale, so the text is always exactly what streaming would produce, 4–8 times faster (see bench/). Each artefact is switched on and named by its directive; the settings in effect at $MESSAGE apply to that message. With LOCALE EXTRA ENABLE (the default) every function has a second overload taking a trailing std::locale const &loc, which selects the language; without it the stream's (for a string: the global) locale does.
| Directive | Default name | Generates |
|---|---|---|
$STRING | <name>Str | std::string <name>Str( parameters ) |
$STREAMABLE | <name>Streamable | a class holding the parameters, with getters, operator<< and a virtual PrintOn( std::ostream & ) |
$ERROR | <name>Error | void <name>Error( parameters ): writes the text on std::cerr, then runs the EXIT statement (default std::exit( EXIT_FAILURE )) |
$EXCEPTION | <name>Exception | an exception class holding the parameters: What() (a std::string), what() (as std::exception::what()), getters |
$THROW | Throw<name> | [[noreturn]] void Throw<name>( parameters ): throws the exception class (generated for $THROW even when $EXCEPTION is disabled) |
$SYSLOG | Log<name> | void Log<name>( parameters, int option_parameter = FACILITY \| LEVEL ): sends the text to syslog() |
(the same for $STREAMABLE, $ERROR, $EXCEPTION, $THROW, $SYSLOG). The first PREFIX / POSTFIX / NO… of an artefact replaces both defaults: after $STRING PREFIX get, message Hello gives getHello, not getHelloStr. Several options may share a line: $STRING ENABLE PREFIX get.
By default an exception class gets What() (the text as a std::string) and what(). When its base already implements what() on top of a pure virtual text method — virtual std::string name( ) const noexcept — OVERRIDE name implements that method instead (override, noexcept) and generates neither What() nor what(). It needs an INHERITED base.
(PARENT is a synonym of INHERITED.) The access defaults to PUBLIC, so catch ( std::exception const & ) catches a class derived from std::exception. The base is default-constructed. A streamable class overrides the base's virtual PrintOn( std::ostream & ) const, if it has one.
MEMBER OF defines class::<function>( parameters... ); declare it in your class (and $INCLUDE its header). C++ requires the class to be in the $NAMESPACE or in a namespace nested in it.MEMBER AS name declares and defines name() in the message's streamable class — it uses the stored parameters, so it takes none. Needs $STREAMABLE ENABLE.CONST (the default) makes it a const member function.EXIT takes the rest of the line, so it is the last option on its line. The defaults are std::exit( EXIT_FAILURE ), USER and INFO.
$MODULE names the output files: name.hh and name.cpp by default (mstring -m overrides the name). NOHEADER writes the declarations into the source, which then compiles on its own. INLINE writes a header-only module: every definition is inline in the header, no source file (several INLINE modules can be included into one translation unit). NOSOURCE — the header without any definitions — is deprecated (a warning); use INLINE.$NAMESPACE a::b puts everything into namespace a { namespace b {; an empty $NAMESPACE goes back to the global namespace. The last one wins.$EXPORT MACRO puts a visibility macro before every generated class (class MACRO Name) and free-function declaration — for code built into a shared library with hidden visibility, or a DLL. $INCLUDE the header that defines the macro. $EXPORT alone removes it. The last one wins.$INCLUDE adds an #include to the header.$USING adds using name; inside the namespace of the header (the parameter types may rely on it).$IMPORT parses another message file as if its lines were here — its settings and messages count. It is searched next to the importing file, then in the working folder, then in the -I folders. The name may be quoted.| Option | |
|---|---|
-m <module> | the module name of the output files (overrides $MODULE) |
-O <folder> | write the output files into <folder> (it must exist) |
-I <folders> | $IMPORT search folders, :-separated; may be repeated |
-t | trace: print every $MESSAGE on stderr |
-w | do not print warnings |
-v, -h | version, help |
Without a file (or with - ) the input is standard input. An output file is only rewritten when its content changes, so an unchanged message file does not trigger a rebuild. Errors are reported as file:line:column: error: ... (warnings as ... warning: ...); the exit code is 1 on any error, warnings do not change it.