ROSE 0.11.145.147
DocumentMarkup.h
1// WARNING: Changes to this file must be contributed back to Sawyer or else they will
2// be clobbered by the next update from Sawyer. The Sawyer repository is at
3// https://gitlab.com/charger7534/sawyer.git.
4
5
6
7
8#ifndef Sawyer_Document_Markup_H
9#define Sawyer_Document_Markup_H
10
11#include <Sawyer/Exception.h>
12#include <Sawyer/Lexer.h>
13#include <Sawyer/LineVector.h>
14#include <Sawyer/Map.h>
15#include <Sawyer/Sawyer.h>
16
17#include <boost/algorithm/string/join.hpp>
18#include <boost/regex.hpp>
19
20namespace Sawyer {
21
26namespace Document {
27
34namespace Markup {
35
36class Grammar;
37
39// Token
41
42enum TokenType {
43 TOK_DATA, // anything that's not markup
44 TOK_FUNCTION, // function name, like "code" scanned from "@code"
45 TOK_LEFT, // unescaped left paren (escaped are part of TOK_DATA)
46 TOK_RIGHT, // unescaped right paren
47 TOK_BLANK_LINE // one or more lines with no non-white-space
48};
49
51
53// TokenStream
55
56class SAWYER_EXPORT TokenStream: public Lexer::TokenStream<Token> {
57 static const char CHAR_LEFT = '{';
58 static const char CHAR_RIGHT = '}';
59 static const char CHAR_AT = '@';
60
61public:
62 explicit TokenStream(const std::string &s): Lexer::TokenStream<Token>(s) {}
63 Token scanNextToken(const Container::LineVector &content, size_t &at /*in,out*/);
64};
65
67// Function
69
71class SAWYER_EXPORT Function: public SharedObject, public SharedFromThis<Function> {
72public:
75
76private:
77 // Declaration for a formal argument
78 struct FormalArg {
79 std::string name; // name of argument for diagnostics and debugging
80 Optional<std::string> dflt; // default value
81
82 explicit FormalArg(const std::string &name, const Optional<std::string> &dflt = Sawyer::Nothing())
83 : name(name), dflt(dflt) {}
84
85 bool isRequired() const { if (dflt) return false; else return true; }
86 bool isOptional() const { return !isRequired(); }
87 };
88
89 std::string name_; // name of function for diagnostics and debugging
90 bool isMacro_; // if true, then don't evaluate args before calling
91 std::vector<FormalArg> formals_; // declarations of formal arguments
92 size_t ellipsis_; // max number of additional args
93
94protected:
95 explicit Function(const std::string &name, bool evalArgs = true)
96 : name_(name), isMacro_(!evalArgs), ellipsis_(0) {}
97
98public:
102 const std::string& name() const;
103
107 bool isMacro() const;
108
112 Ptr arg(const std::string &name);
113
121 Ptr arg(const std::string &name, const std::string &dflt);
122
127 Ptr ellipsis(size_t n = (size_t)(-1));
128
130 size_t nRequiredArgs() const;
131
136 size_t nOptionalArgs() const;
137
142 size_t nAdditionalArgs() const;
143
145 size_t maxArgs() const;
146
152 void validateArgs(std::vector<std::string> &actuals /*in,out*/, TokenStream&) const;
153
155 virtual std::string eval(const Grammar&, const std::vector<std::string> &actuals) = 0;
156};
157
159// Errors and exceptions
161
163class SAWYER_EXPORT ErrorLocation {
164 friend class Grammar;
165
166 // Stack frame where error occurred
167 struct Frame {
168 std::string name; // like "in @foo at name_of_input"
169 size_t lineIdx; // zero-origin line number
170 size_t offset; // beginning position within the line
171 std::string input; // line of input causing error
172 Frame(TokenStream &where, const std::string &mesg);
173 };
174
175 // Use the destructor to add a record to the stack frame. We could have used try/catch but that would interfere with
176 // debugging the exception. The alternative is that each function that could add a stack frame does so like this:
177 // void some_function(ErrorLocation &eloc, TokenStream &tokens, ...) {
178 // Trap trap(eloc, tokens);
179 // do_something_that_might_throw();
180 // trap.passed();
181 // }
182 //
183 // Any failure to reach trap.passed() will add a stack frame to the location.
184 class Trap {
185 ErrorLocation &eloc_;
186 TokenStream &tokens_;
187 std::string mesg_;
188 bool passed_;
189
190 public:
191 Trap(ErrorLocation &eloc, TokenStream &tokens, const std::string &mesg)
192 : eloc_(eloc), tokens_(tokens), mesg_(mesg), passed_(false) {}
193 ~Trap() { if (!passed_) eloc_.push(Frame(tokens_, mesg_)); }
194 void passed() { passed_ = true; }
195 };
196
197private:
198 std::vector<Frame> frames_;
199
200 void push(const Frame &frame) {
201 frames_.push_back(frame);
202 }
203
204public:
206 std::string toString() const;
207};
208
210class SAWYER_EXPORT SyntaxError: public Sawyer::Exception::SyntaxError {
211 ErrorLocation eloc_;
212public:
213 ~SyntaxError() throw () {}
214
216 SyntaxError(const std::string &mesg)
217 : Sawyer::Exception::SyntaxError(mesg) {}
218
220 void errorLocation(const ErrorLocation &eloc) {
221 eloc_ = eloc;
222 }
223};
224
226// Predefined functions
228
232class SAWYER_EXPORT StaticContent: public Function {
233 std::string resultString_;
234protected:
235 StaticContent(const std::string &name, const std::string &resultString)
236 : Function(name), resultString_(resultString) {}
237public:
239 static Ptr instance(const std::string &name, const std::string str) {
240 return Ptr(new StaticContent(name, str));
241 }
242 std::string eval(const Grammar&, const std::vector<std::string> &args);
243};
244
249class SAWYER_EXPORT Error: public Function {
250protected:
251 Error(const std::string &name): Function(name) {}
252public:
254 static Ptr instance(const std::string &name, const std::string dfltMesg = "error") {
255 return Ptr(new Error(name))->arg("message", dfltMesg);
256 }
257 std::string eval(const Grammar&, const std::vector<std::string> &args);
258};
259
264class SAWYER_EXPORT Quote: public Function {
265protected:
266 Quote(const std::string &name): Function(name, false) {}
267public:
269 static Ptr instance(const std::string &name) {
270 return Ptr(new Quote(name))->ellipsis();
271 }
272 std::string eval(const Grammar&, const std::vector<std::string> &args);
273};
274
280class SAWYER_EXPORT Eval: public Function {
281protected:
282 Eval(const std::string &name): Function(name) {}
283public:
285 static Ptr instance(const std::string &name) {
286 return Ptr(new Eval(name))->ellipsis();
287 }
288 std::string eval(const Grammar &grammar, const std::vector<std::string> &args);
289};
290
295class SAWYER_EXPORT IfEq: public Function {
296protected:
297 IfEq(const std::string &name): Function(name, false) {}
298public:
299 static Ptr instance(const std::string &name) {
300 return Ptr(new IfEq(name))->arg("val1")->arg("val2")->arg("if_part")->arg("else_part", "");
301 }
302 std::string eval(const Grammar &grammar, const std::vector<std::string> &args);
303};
304
308class SAWYER_EXPORT Concat: public Function {
309protected:
310 Concat(const std::string &name): Function(name) {}
311public:
312 static Ptr instance(const std::string &name) {
313 return Ptr(new Concat(name))->ellipsis();
314 }
315 std::string eval(const Grammar &grammar, const std::vector<std::string> &args);
316};
317
319// Reflow
321
327class SAWYER_EXPORT Reflow {
328 size_t indentLevel_;
329 std::string indentation_; // string for one level of indentation
330 std::ostringstream out_;
331 size_t column_;
332 std::string spaces_; // accumulated white space
333 std::string nonspaces_; // non-spaces following accumulated space
334 size_t pageWidth_;
335 size_t nLineFeeds_; // number of consecutive linefeeds
336
337public:
341 explicit Reflow(size_t pageWidth = 80)
342 : indentLevel_(0), indentation_(" "), column_(0), pageWidth_(pageWidth), nLineFeeds_(0) {}
343
351 size_t pageWidth() const { return pageWidth_; }
352 Reflow& pageWidth(size_t n) { pageWidth_ = std::max(n, (size_t)20); return *this; }
361 const std::string& indentationString() const { return indentation_; }
362 Reflow& indentationString(const std::string &s) { indentation_ = s; return *this; }
381
385 Reflow& operator()(const std::string &s);
386
390 std::string toString();
391
392private:
393 void emitIndentation(); // indent if we're at the beginning of a line. Also discards accumulated white space.
394 void emitAccumulated(); // optionally indent and emit accumulated space and non-space
395 void emitNewLine(); // advance to the next line without emitting accumulated text or indentation.
396};
397
399// Grammar
401
403class SAWYER_EXPORT Grammar {
404 Container::Map<std::string, Function::Ptr> functions_; // functions indexed by their names
405 static const bool CONSUME = true;
406 static const bool LEAVE = false;
407
408public:
409 virtual ~Grammar() {}
410
415
417 virtual std::string operator()(const std::string &s) const;
418
420 static std::string unescape(const std::string &s);
421
423 static std::string escape(const std::string &s);
424
425private:
426 // Evaluate an entire token stream. Throws an exception if an error occurs.
427 std::string eval(TokenStream &tokens, ErrorLocation&) const;
428
429 // Returns a string up to (and possibly including) the next CHAR_RIGHT that is not balanced by a CHAR_LEFT encountered
430 // during the scanning. The TOK_RIGHT is consumed if requireRight is set, and an error is thrown if that token is not
431 // found.
432 std::string readArgument(TokenStream&, ErrorLocation&, bool requireRight) const;
433
434 // Parse one argument by parsing up to (and possibly including) the next unbalanced TOK_RIGHT. The TOK_RIGHT is consumed
435 // if requireRight is set, and an error is thrown if that token is not found.
436 std::string evalArgument(TokenStream&, ErrorLocation&, bool requireRight) const;
437
438 // Parse one function. The current token should be a TOK_FUNCTION.
439 std::string evalFunction(TokenStream&, ErrorLocation&) const;
440};
441
442} // namespace
443} // namespace
444} // namespace
445
446#endif
A buffer of characters indexed by line number.
Definition LineVector.h:24
Container associating values with keys.
Definition Sawyer/Map.h:72
std::string eval(const Grammar &grammar, const std::vector< std::string > &args)
How to evaluate this function or macro.
Information about the location of an exception.
std::string toString() const
Convert an error location to an error string.
Function that generates an error message.
static Ptr instance(const std::string &name, const std::string dfltMesg="error")
Create a new instance.
std::string eval(const Grammar &, const std::vector< std::string > &args)
How to evaluate this function or macro.
Evaluate arguments a second time.
static Ptr instance(const std::string &name)
Create a new instance.
std::string eval(const Grammar &grammar, const std::vector< std::string > &args)
How to evaluate this function or macro.
Base class for markup functions.
size_t nOptionalArgs() const
Number of optional arguments.
size_t maxArgs() const
Max number of actual arguments possible.
size_t nRequiredArgs() const
Number of required arguments.
size_t nAdditionalArgs() const
Max number of additional arguments.
Ptr arg(const std::string &name, const std::string &dflt)
Declare an optional argument.
Ptr ellipsis(size_t n=(size_t)(-1))
Declare additional arguments.
void validateArgs(std::vector< std::string > &actuals, TokenStream &) const
Check and adjust actual arguments.
SharedPointer< Function > Ptr
Reference-counting pointer to markup function.
virtual std::string eval(const Grammar &, const std::vector< std::string > &actuals)=0
How to evaluate this function or macro.
bool isMacro() const
Whether declaration is for a macro.
Ptr arg(const std::string &name)
Declare a required argument.
const std::string & name() const
Function name.
Grammar & with(const Function::Ptr &)
Insert function.
static std::string escape(const std::string &s)
Escape all special "@", "{", and "}".
static std::string unescape(const std::string &s)
Expand escape sequences "@@", "@{" and "@}".
virtual std::string operator()(const std::string &s) const
Evaluate an entire string.
std::string eval(const Grammar &grammar, const std::vector< std::string > &args)
How to evaluate this function or macro.
Function that quotes its arguments.
static Ptr instance(const std::string &name)
Create a new instance.
std::string eval(const Grammar &, const std::vector< std::string > &args)
How to evaluate this function or macro.
A class that can reflow and indent paragraphs.
size_t pageWidth() const
Property: Page width.
Reflow & lineBreak()
Insert a line break.
Reflow & operator()(const std::string &s)
Insert text.
Reflow & pageWidth(size_t n)
Property: Page width.
Reflow & indentationString(const std::string &s)
Property: Indentation string.
Reflow & operator--()
Increase or decrease indentation.
Reflow(size_t pageWidth=80)
Construct a reflow filter.
std::string toString()
Extract the reflowed string.
Reflow & operator++()
Increase or decrease indentation.
const std::string & indentationString() const
Property: Indentation string.
A function that inserts a string.
std::string eval(const Grammar &, const std::vector< std::string > &args)
How to evaluate this function or macro.
static Ptr instance(const std::string &name, const std::string str)
Create a new instance.
Syntax error when parsing markup.
SyntaxError(const std::string &mesg)
Syntax error.
void errorLocation(const ErrorLocation &eloc)
Set an error location.
Token scanNextToken(const Container::LineVector &content, size_t &at)
Function that obtains the next token.
Error in parsing something.
An ordered list of tokens scanned from input.
Definition Lexer.h:91
Represents one token of input.
Definition Lexer.h:28
Represents no value.
Definition Optional.h:36
Holds a value or nothing.
Definition Optional.h:56
Creates SharedPointer from this.
Base class for reference counted objects.
Reference-counting intrusive smart pointer.
Sawyer support library.