1 // This file is distributed under the BSD License.
2 // See "license.txt" for details.
3 // Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com)
4 // Copyright 2009-2017, Jason Turner (jason@emptycrate.com)
5 // http://www.chaiscript.com
6 
7 // This is an open source non-commercial project. Dear PVS-Studio, please check it.
8 // PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
9 
10 
11 #ifndef CHAISCRIPT_PARSER_HPP_
12 #define CHAISCRIPT_PARSER_HPP_
13 
14 #include <exception>
15 #include <iostream>
16 #include <memory>
17 #include <sstream>
18 #include <string>
19 #include <vector>
20 #include <cctype>
21 #include <cstring>
22 
23 
24 
25 
26 #include "../dispatchkit/boxed_value.hpp"
27 #include "chaiscript_common.hpp"
28 #include "chaiscript_optimizer.hpp"
29 #include "chaiscript_tracer.hpp"
30 #include "../utility/fnv1a.hpp"
31 #include "../utility/static_string.hpp"
32 
33 #if defined(CHAISCRIPT_UTF16_UTF32)
34 #include <locale>
35 #include <codecvt>
36 #endif
37 
38 #if defined(CHAISCRIPT_MSVC) && defined(max) && defined(min)
39 #define CHAISCRIPT_PUSHED_MIN_MAX
40 #pragma push_macro("max") // Why Microsoft? why? This is worse than bad
41 #undef max
42 #pragma push_macro("min")
43 #undef min
44 #endif
45 
46 
47 namespace chaiscript
48 {
49   /// \brief Classes and functions used during the parsing process.
50   namespace parser
51   {
52     /// \brief Classes and functions internal to the parsing process. Not supported for the end user.
53     namespace detail
54     {
55       enum Alphabet
56       {   symbol_alphabet = 0
57         ,   keyword_alphabet
58           ,   int_alphabet
59           ,   float_alphabet
60           ,   x_alphabet
61           ,   hex_alphabet
62           ,   b_alphabet
63           ,   bin_alphabet
64           ,   id_alphabet
65           ,   white_alphabet
66           ,   int_suffix_alphabet
67           ,   float_suffix_alphabet
68           ,   max_alphabet
69           ,   lengthof_alphabet = 256
70       };
71 
72       // Generic for u16, u32 and wchar
73       template<typename string_type>
74       struct Char_Parser_Helper
75       {
76         // common for all implementations
u8str_from_llchaiscript::parser::detail::Char_Parser_Helper77         static std::string u8str_from_ll(long long val)
78         {
79           typedef std::string::value_type char_type;
80 
81           char_type c[2];
82           c[1] = char_type(val);
83           c[0] = char_type(val >> 8);
84 
85           if (c[0] == 0)
86           {
87             return std::string(1, c[1]); // size, character
88           }
89 
90           return std::string(c, 2); // char buffer, size
91         }
92 
str_from_llchaiscript::parser::detail::Char_Parser_Helper93         static string_type str_from_ll(long long val)
94         {
95           typedef typename string_type::value_type target_char_type;
96 #if defined (CHAISCRIPT_UTF16_UTF32)
97           // prepare converter
98           std::wstring_convert<std::codecvt_utf8<target_char_type>, target_char_type> converter;
99           // convert
100           return converter.from_bytes(u8str_from_ll(val));
101 #else
102           // no conversion available, just put value as character
103           return string_type(1, target_char_type(val)); // size, character
104 #endif
105         }
106       };
107 
108       // Specialization for char AKA UTF-8
109       template<>
110       struct Char_Parser_Helper<std::string>
111       {
str_from_llchaiscript::parser::detail::Char_Parser_Helper112         static std::string str_from_ll(long long val)
113         {
114           // little SFINAE trick to avoid base class
115           return Char_Parser_Helper<std::true_type>::u8str_from_ll(val);
116         }
117       };
118     }
119 
120 
121     template<typename Tracer, typename Optimizer>
122     class ChaiScript_Parser final : public ChaiScript_Parser_Base {
get_tracer_ptr()123       void *get_tracer_ptr() override {
124         return &m_tracer;
125       }
126 
build_alphabet()127       static std::array<std::array<bool, detail::lengthof_alphabet>, detail::max_alphabet> build_alphabet()
128       {
129         std::array<std::array<bool, detail::lengthof_alphabet>, detail::max_alphabet> alphabet;
130 
131         for (auto &alpha : alphabet) {
132           alpha.fill(false);
133         }
134 
135         alphabet[detail::symbol_alphabet][static_cast<size_t>('?')]=true;
136         alphabet[detail::symbol_alphabet][static_cast<size_t>('+')]=true;
137         alphabet[detail::symbol_alphabet][static_cast<size_t>('-')]=true;
138         alphabet[detail::symbol_alphabet][static_cast<size_t>('*')]=true;
139         alphabet[detail::symbol_alphabet][static_cast<size_t>('/')]=true;
140         alphabet[detail::symbol_alphabet][static_cast<size_t>('|')]=true;
141         alphabet[detail::symbol_alphabet][static_cast<size_t>('&')]=true;
142         alphabet[detail::symbol_alphabet][static_cast<size_t>('^')]=true;
143         alphabet[detail::symbol_alphabet][static_cast<size_t>('=')]=true;
144         alphabet[detail::symbol_alphabet][static_cast<size_t>('.')]=true;
145         alphabet[detail::symbol_alphabet][static_cast<size_t>('<')]=true;
146         alphabet[detail::symbol_alphabet][static_cast<size_t>('>')]=true;
147 
148         for ( size_t c = 'a' ; c <= 'z' ; ++c ) { alphabet[detail::keyword_alphabet][c]=true; }
149         for ( size_t c = 'A' ; c <= 'Z' ; ++c ) { alphabet[detail::keyword_alphabet][c]=true; }
150         for ( size_t c = '0' ; c <= '9' ; ++c ) { alphabet[detail::keyword_alphabet][c]=true; }
151         alphabet[detail::keyword_alphabet][static_cast<size_t>('_')]=true;
152 
153         for ( size_t c = '0' ; c <= '9' ; ++c ) { alphabet[detail::int_alphabet][c]=true; }
154         for ( size_t c = '0' ; c <= '9' ; ++c ) { alphabet[detail::float_alphabet][c]=true; }
155         alphabet[detail::float_alphabet][static_cast<size_t>('.')]=true;
156 
157         for ( size_t c = '0' ; c <= '9' ; ++c ) { alphabet[detail::hex_alphabet][c]=true; }
158         for ( size_t c = 'a' ; c <= 'f' ; ++c ) { alphabet[detail::hex_alphabet][c]=true; }
159         for ( size_t c = 'A' ; c <= 'F' ; ++c ) { alphabet[detail::hex_alphabet][c]=true; }
160 
161         alphabet[detail::x_alphabet][static_cast<size_t>('x')]=true;
162         alphabet[detail::x_alphabet][static_cast<size_t>('X')]=true;
163 
164         for ( size_t c = '0' ; c <= '1' ; ++c ) { alphabet[detail::bin_alphabet][c]=true; }
165         alphabet[detail::b_alphabet][static_cast<size_t>('b')]=true;
166         alphabet[detail::b_alphabet][static_cast<size_t>('B')]=true;
167 
168         for ( size_t c = 'a' ; c <= 'z' ; ++c ) { alphabet[detail::id_alphabet][c]=true; }
169         for ( size_t c = 'A' ; c <= 'Z' ; ++c ) { alphabet[detail::id_alphabet][c]=true; }
170         alphabet[detail::id_alphabet][static_cast<size_t>('_')] = true;
171 
172         alphabet[detail::white_alphabet][static_cast<size_t>(' ')]=true;
173         alphabet[detail::white_alphabet][static_cast<size_t>('\t')]=true;
174 
175         alphabet[detail::int_suffix_alphabet][static_cast<size_t>('l')] = true;
176         alphabet[detail::int_suffix_alphabet][static_cast<size_t>('L')] = true;
177         alphabet[detail::int_suffix_alphabet][static_cast<size_t>('u')] = true;
178         alphabet[detail::int_suffix_alphabet][static_cast<size_t>('U')] = true;
179 
180         alphabet[detail::float_suffix_alphabet][static_cast<size_t>('l')] = true;
181         alphabet[detail::float_suffix_alphabet][static_cast<size_t>('L')] = true;
182         alphabet[detail::float_suffix_alphabet][static_cast<size_t>('f')] = true;
183         alphabet[detail::float_suffix_alphabet][static_cast<size_t>('F')] = true;
184 
185         return alphabet;
186       }
187 
create_alphabet()188       static const std::array<std::array<bool, detail::lengthof_alphabet>, detail::max_alphabet> &create_alphabet()
189       {
190         static const auto alpha = build_alphabet();
191         return alpha;
192       }
193 
194 
create_operator_matches()195       static const std::vector<std::vector<utility::Static_String>> &create_operator_matches() {
196         static const std::vector<std::vector<utility::Static_String>> operator_matches {
197           {"?"},
198           {"||"},
199           {"&&"},
200           {"|"},
201           {"^"},
202           {"&"},
203           {"==", "!="},
204           {"<", "<=", ">", ">="},
205           {"<<", ">>"},
206           //We share precedence here but then separate them later
207           {"+", "-"},
208           {"*", "/", "%"},
209           {"++", "--", "-", "+", "!", "~"}
210         };
211 
212         return operator_matches;
213       }
214 
215 
create_operators()216       static const std::array<Operator_Precidence, 12> &create_operators() {
217         static const std::array<Operator_Precidence, 12> operators = { {
218           Operator_Precidence::Ternary_Cond,
219           Operator_Precidence::Logical_Or,
220           Operator_Precidence::Logical_And,
221           Operator_Precidence::Bitwise_Or,
222           Operator_Precidence::Bitwise_Xor,
223           Operator_Precidence::Bitwise_And,
224           Operator_Precidence::Equality,
225           Operator_Precidence::Comparison,
226           Operator_Precidence::Shift,
227           Operator_Precidence::Addition,
228           Operator_Precidence::Multiplication,
229           Operator_Precidence::Prefix
230         } };
231         return operators;
232       }
233 
multiline_comment_end()234       static const utility::Static_String &multiline_comment_end()
235       {
236         static const utility::Static_String s("*/");
237         return s;
238       }
239 
multiline_comment_begin()240       static const utility::Static_String &multiline_comment_begin()
241       {
242         static const utility::Static_String s("/*");
243         return s;
244       }
245 
singleline_comment()246       static const utility::Static_String &singleline_comment()
247       {
248         static const utility::Static_String s("//");
249         return s;
250       }
251 
annotation()252       static const utility::Static_String &annotation()
253       {
254         static const utility::Static_String s("#");
255         return s;
256       }
257 
cr_lf()258       static const utility::Static_String &cr_lf()
259       {
260         static const utility::Static_String s("\r\n");
261         return s;
262       }
263 
264       const std::array<std::array<bool, detail::lengthof_alphabet>, detail::max_alphabet> &m_alphabet = create_alphabet();
265       const std::vector<std::vector<utility::Static_String>> &m_operator_matches = create_operator_matches();
266       const std::array<Operator_Precidence, 12> &m_operators = create_operators();
267 
268       std::shared_ptr<std::string> m_filename;
269       std::vector<eval::AST_Node_Impl_Ptr<Tracer>> m_match_stack;
270 
271 
272       struct Position
273       {
274         Position() = default;
275 
Positionchaiscript::parser::ChaiScript_Parser::Position276         Position(std::string::const_iterator t_pos, std::string::const_iterator t_end)
277           : line(1), col(1), m_pos(t_pos), m_end(t_end), m_last_col(1)
278         {
279         }
280 
strchaiscript::parser::ChaiScript_Parser::Position281         static std::string str(const Position &t_begin, const Position &t_end) {
282           return std::string(t_begin.m_pos, t_end.m_pos);
283         }
284 
operator ++chaiscript::parser::ChaiScript_Parser::Position285         Position &operator++() {
286           if (m_pos != m_end) {
287             if (*m_pos == '\n') {
288               ++line;
289               m_last_col = std::exchange(col, 1);
290             } else {
291               ++col;
292             }
293 
294             ++m_pos;
295           }
296           return *this;
297         }
298 
operator --chaiscript::parser::ChaiScript_Parser::Position299         Position &operator--() {
300           --m_pos;
301           if (*m_pos == '\n') {
302             --line;
303             col = m_last_col;
304           } else {
305             --col;
306           }
307           return *this;
308         }
309 
operator +=chaiscript::parser::ChaiScript_Parser::Position310         Position &operator+=(size_t t_distance) {
311           *this = (*this) + t_distance;
312           return *this;
313         }
314 
operator +chaiscript::parser::ChaiScript_Parser::Position315         Position operator+(size_t t_distance) const {
316           Position ret(*this);
317           for (size_t i = 0; i < t_distance; ++i) {
318             ++ret;
319           }
320           return ret;
321         }
322 
operator -=chaiscript::parser::ChaiScript_Parser::Position323         Position &operator-=(size_t t_distance) {
324           *this = (*this) - t_distance;
325           return *this;
326         }
327 
operator -chaiscript::parser::ChaiScript_Parser::Position328         Position operator-(size_t t_distance) const {
329           Position ret(*this);
330           for (size_t i = 0; i < t_distance; ++i) {
331             --ret;
332           }
333           return ret;
334         }
335 
operator ==chaiscript::parser::ChaiScript_Parser::Position336         bool operator==(const Position &t_rhs) const {
337           return m_pos == t_rhs.m_pos;
338         }
339 
operator !=chaiscript::parser::ChaiScript_Parser::Position340         bool operator!=(const Position &t_rhs) const {
341           return m_pos != t_rhs.m_pos;
342         }
343 
has_morechaiscript::parser::ChaiScript_Parser::Position344         bool has_more() const {
345           return m_pos != m_end;
346         }
347 
remainingchaiscript::parser::ChaiScript_Parser::Position348         size_t remaining() const {
349           return static_cast<size_t>(std::distance(m_pos, m_end));
350         }
351 
operator *chaiscript::parser::ChaiScript_Parser::Position352         const char& operator*() const {
353           if (m_pos == m_end) {
354             static const char ktmp ='\0';
355             return ktmp;
356           } else {
357             return *m_pos;
358           }
359         }
360 
361         int line = -1;
362         int col = -1;
363 
364         private:
365           std::string::const_iterator m_pos;
366           std::string::const_iterator m_end;
367           int m_last_col = -1;
368       };
369 
370       Position m_position;
371 
372       Tracer m_tracer;
373       Optimizer m_optimizer;
374 
validate_object_name(const std::string & name) const375       void validate_object_name(const std::string &name) const
376       {
377         if (!Name_Validator::valid_object_name(name)) {
378           throw exception::eval_error("Invalid Object Name: " + name, File_Position(m_position.line, m_position.col), *m_filename);
379         }
380       }
381 
382       public:
ChaiScript_Parser(Tracer tracer=Tracer (),Optimizer optimizer=Optimizer ())383       explicit ChaiScript_Parser(Tracer tracer = Tracer(), Optimizer optimizer=Optimizer())
384         : m_tracer(std::move(tracer)),
385           m_optimizer(std::move(optimizer))
386       {
387         m_match_stack.reserve(2);
388       }
389 
get_tracer()390       Tracer &get_tracer()
391       {
392         return m_tracer;
393       }
394 
get_optimizer()395       Optimizer &get_optimizer()
396       {
397         return m_optimizer;
398       }
399 
400       ChaiScript_Parser(const ChaiScript_Parser &) = delete;
401       ChaiScript_Parser &operator=(const ChaiScript_Parser &) = delete;
402       ChaiScript_Parser(ChaiScript_Parser &&) = default;
403       ChaiScript_Parser &operator=(ChaiScript_Parser &&) = delete;
404 
405       /// test a char in an m_alphabet
char_in_alphabet(char c,detail::Alphabet a) const406       bool char_in_alphabet(char c, detail::Alphabet a) const { return m_alphabet[a][static_cast<uint8_t>(c)]; }
407 
408       /// Prints the parsed ast_nodes as a tree
debug_print(const AST_Node & t,std::string prepend="") const409       void debug_print(const AST_Node &t, std::string prepend = "") const override {
410         std::cout << prepend << "(" << ast_node_type_to_string(t.identifier) << ") " << t.text << " : " << t.start().line << ", " << t.start().column << '\n';
411         for (const auto &node : t.get_children()) {
412           debug_print(node.get(), prepend + "  ");
413         }
414       }
415 
416 
417       /// Helper function that collects ast_nodes from a starting position to the top of the stack into a new AST node
418       template<typename NodeType>
build_match(size_t t_match_start,std::string t_text="")419       void build_match(size_t t_match_start, std::string t_text = "") {
420         bool is_deep = false;
421 
422         Parse_Location filepos = [&]()->Parse_Location{
423           //so we want to take everything to the right of this and make them children
424           if (t_match_start != m_match_stack.size()) {
425             is_deep = true;
426             return Parse_Location(
427                 m_filename,
428                 m_match_stack[t_match_start]->location.start.line,
429                 m_match_stack[t_match_start]->location.start.column,
430                 m_position.line,
431                 m_position.col
432               );
433           } else {
434             return Parse_Location(
435                 m_filename,
436                 m_position.line,
437                 m_position.col,
438                 m_position.line,
439                 m_position.col
440               );
441           }
442         }();
443 
444         std::vector<eval::AST_Node_Impl_Ptr<Tracer>> new_children;
445 
446         if (is_deep) {
447           new_children.assign(std::make_move_iterator(m_match_stack.begin() + static_cast<int>(t_match_start)),
448                               std::make_move_iterator(m_match_stack.end()));
449           m_match_stack.erase(m_match_stack.begin() + static_cast<int>(t_match_start), m_match_stack.end());
450         }
451 
452         /// \todo fix the fact that a successful match that captured no ast_nodes doesn't have any real start position
453         m_match_stack.push_back(
454             m_optimizer.optimize(
455               chaiscript::make_unique<chaiscript::eval::AST_Node_Impl<Tracer>, NodeType>(
456                 std::move(t_text),
457                 std::move(filepos),
458                 std::move(new_children)))
459             );
460       }
461 
462 
463       /// Reads a symbol group from input if it matches the parameter, without skipping initial whitespace
Symbol_(const utility::Static_String & sym)464       inline auto Symbol_(const utility::Static_String &sym)
465       {
466         const auto len = sym.size();
467         if (m_position.remaining() >= len) {
468           const char *file_pos = &(*m_position);
469           for (size_t pos = 0; pos < len; ++pos)
470           {
471             if (sym.c_str()[pos] != file_pos[pos]) { return false; }
472           }
473           m_position += len;
474           return true;
475         }
476         return false;
477       }
478 
479       /// Skips any multi-line or single-line comment
SkipComment()480       bool SkipComment() {
481         if (Symbol_(multiline_comment_begin())) {
482           while (m_position.has_more()) {
483             if (Symbol_(multiline_comment_end())) {
484               break;
485             } else if (!Eol_()) {
486               ++m_position;
487             }
488           }
489           return true;
490         } else if (Symbol_(singleline_comment())) {
491           while (m_position.has_more()) {
492             if (Symbol_(cr_lf())) {
493               m_position -= 2;
494               break;
495             } else if (Char_('\n')) {
496               --m_position;
497               break;
498             } else {
499               ++m_position;
500             }
501           }
502           return true;
503         } else if (Symbol_(annotation())) {
504           while (m_position.has_more()) {
505             if (Symbol_(cr_lf())) {
506               m_position -= 2;
507               break;
508             } else if (Char_('\n')) {
509               --m_position;
510               break;
511             } else {
512               ++m_position;
513             }
514           }
515           return true;
516         }
517         return false;
518       }
519 
520 
521       /// Skips ChaiScript whitespace, which means space and tab, but not cr/lf
522       /// jespada: Modified SkipWS to skip optionally CR ('\n') and/or LF+CR ("\r\n")
SkipWS(bool skip_cr=false)523       bool SkipWS(bool skip_cr=false) {
524         bool retval = false;
525 
526         while (m_position.has_more()) {
527           auto end_line = (*m_position != 0) && ((*m_position == '\n') || (*m_position == '\r' && *(m_position+1) == '\n'));
528 
529           if ( char_in_alphabet(*m_position,detail::white_alphabet) || (skip_cr && end_line)) {
530 
531             if(end_line) {
532               if(*m_position == '\r') {
533                 // discards lf
534                 ++m_position;
535               }
536             }
537 
538             ++m_position;
539 
540             retval = true;
541           }
542           else if (SkipComment()) {
543             retval = true;
544           } else {
545             break;
546           }
547         }
548         return retval;
549       }
550 
551       /// Reads the optional exponent (scientific notation) and suffix for a Float
read_exponent_and_suffix()552       bool read_exponent_and_suffix() {
553         // Support a form of scientific notation: 1e-5, 35.5E+8, 0.01e19
554         if (m_position.has_more() && (std::tolower(*m_position) == 'e')) {
555           ++m_position;
556           if (m_position.has_more() && ((*m_position == '-') || (*m_position == '+'))) {
557             ++m_position;
558           }
559           auto exponent_pos = m_position;
560           while (m_position.has_more() && char_in_alphabet(*m_position,detail::int_alphabet) ) {
561             ++m_position;
562           }
563           if (m_position == exponent_pos) {
564             // Require at least one digit after the exponent
565             return false;
566           }
567         }
568 
569         // Parse optional float suffix
570         while (m_position.has_more() && char_in_alphabet(*m_position, detail::float_suffix_alphabet))
571         {
572           ++m_position;
573         }
574 
575         return true;
576       }
577 
578 
579       /// Reads a floating point value from input, without skipping initial whitespace
Float_()580       bool Float_() {
581         if (m_position.has_more() && char_in_alphabet(*m_position,detail::float_alphabet) ) {
582           while (m_position.has_more() && char_in_alphabet(*m_position,detail::int_alphabet) ) {
583             ++m_position;
584           }
585 
586           if (m_position.has_more() && (std::tolower(*m_position) == 'e')) {
587             // The exponent is valid even without any decimal in the Float (1e8, 3e-15)
588             return read_exponent_and_suffix();
589           }
590           else if (m_position.has_more() && (*m_position == '.')) {
591             ++m_position;
592             if (m_position.has_more() && char_in_alphabet(*m_position,detail::int_alphabet)) {
593               while (m_position.has_more() && char_in_alphabet(*m_position,detail::int_alphabet) ) {
594                 ++m_position;
595               }
596 
597               // After any decimal digits, support an optional exponent (3.7e3)
598               return read_exponent_and_suffix();
599             } else {
600               --m_position;
601             }
602           }
603         }
604         return false;
605       }
606 
607       /// Reads a hex value from input, without skipping initial whitespace
Hex_()608       bool Hex_() {
609         if (m_position.has_more() && (*m_position == '0')) {
610           ++m_position;
611 
612           if (m_position.has_more() && char_in_alphabet(*m_position, detail::x_alphabet) ) {
613             ++m_position;
614             if (m_position.has_more() && char_in_alphabet(*m_position, detail::hex_alphabet)) {
615               while (m_position.has_more() && char_in_alphabet(*m_position, detail::hex_alphabet) ) {
616                 ++m_position;
617               }
618               while (m_position.has_more() && char_in_alphabet(*m_position, detail::int_suffix_alphabet))
619               {
620                 ++m_position;
621               }
622 
623               return true;
624             }
625             else {
626               --m_position;
627             }
628           }
629           else {
630             --m_position;
631           }
632         }
633 
634         return false;
635       }
636 
637       /// Reads an integer suffix
IntSuffix_()638       void IntSuffix_() {
639         while (m_position.has_more() && char_in_alphabet(*m_position, detail::int_suffix_alphabet))
640         {
641           ++m_position;
642         }
643       }
644 
645       /// Reads a binary value from input, without skipping initial whitespace
Binary_()646       bool Binary_() {
647         if (m_position.has_more() && (*m_position == '0')) {
648           ++m_position;
649 
650           if (m_position.has_more() && char_in_alphabet(*m_position, detail::b_alphabet) ) {
651             ++m_position;
652             if (m_position.has_more() && char_in_alphabet(*m_position, detail::bin_alphabet) ) {
653               while (m_position.has_more() && char_in_alphabet(*m_position, detail::bin_alphabet) ) {
654                 ++m_position;
655               }
656               return true;
657             } else {
658               --m_position;
659             }
660           } else {
661             --m_position;
662           }
663         }
664 
665         return false;
666       }
667 
668       /// Parses a floating point value and returns a Boxed_Value representation of it
buildFloat(const std::string & t_val)669       static Boxed_Value buildFloat(const std::string &t_val)
670       {
671         bool float_ = false;
672         bool long_ = false;
673 
674         auto i = t_val.size();
675 
676         for (; i > 0; --i)
677         {
678           char val = t_val[i-1];
679 
680           if (val == 'f' || val == 'F')
681           {
682             float_ = true;
683           } else if (val == 'l' || val == 'L') {
684             long_ = true;
685           } else {
686             break;
687           }
688         }
689 
690         if (float_)
691         {
692           return const_var(parse_num<float>(t_val.substr(0,i)));
693         } else if (long_) {
694           return const_var(parse_num<long double>(t_val.substr(0,i)));
695         } else {
696           return const_var(parse_num<double>(t_val.substr(0,i)));
697         }
698       }
699 
700 
701 
buildInt(const int base,const std::string & t_val,const bool prefixed)702       static Boxed_Value buildInt(const int base, const std::string &t_val, const bool prefixed)
703       {
704         bool unsigned_ = false;
705         bool long_ = false;
706         bool longlong_ = false;
707 
708         auto i = t_val.size();
709 
710         for (; i > 0; --i)
711         {
712           const char val = t_val[i-1];
713 
714           if (val == 'u' || val == 'U')
715           {
716             unsigned_ = true;
717           } else if (val == 'l' || val == 'L') {
718             if (long_)
719             {
720               longlong_ = true;
721             }
722 
723             long_ = true;
724           } else {
725             break;
726           }
727         }
728 
729         const auto val = prefixed?std::string(t_val.begin()+2,t_val.end()):t_val;
730 
731 #ifdef __GNUC__
732 #pragma GCC diagnostic push
733 #pragma GCC diagnostic ignored "-Wsign-compare"
734 
735 #ifdef CHAISCRIPT_CLANG
736 #pragma GCC diagnostic ignored "-Wtautological-compare"
737 #endif
738 
739 #endif
740 
741         try {
742           auto u = std::stoll(val,nullptr,base);
743 
744 
745           if (!unsigned_ && !long_ && u >= std::numeric_limits<int>::min() && u <= std::numeric_limits<int>::max()) {
746             return const_var(static_cast<int>(u));
747           } else if ((unsigned_ || base != 10) && !long_ && u >= std::numeric_limits<unsigned int>::min() && u <= std::numeric_limits<unsigned int>::max()) {
748             return const_var(static_cast<unsigned int>(u));
749           } else if (!unsigned_ && !longlong_ && u >= std::numeric_limits<long>::min() && u <= std::numeric_limits<long>::max()) {
750             return const_var(static_cast<long>(u));
751           } else if ((unsigned_ || base != 10) && !longlong_
752                      && u >= std::numeric_limits<unsigned long>::min()
753                      && u <= std::numeric_limits<unsigned long>::max()) {
754             return const_var(static_cast<unsigned long>(u));
755           } else if (!unsigned_ && u >= std::numeric_limits<long long>::min() && u <= std::numeric_limits<long long>::max()) {
756             return const_var(static_cast<long long>(u));
757           } else {
758             return const_var(static_cast<unsigned long long>(u));
759           }
760 
761         } catch (const std::out_of_range &) {
762           // too big to be signed
763           try {
764             auto u = std::stoull(val,nullptr,base);
765 
766             if (!longlong_ && u >= std::numeric_limits<unsigned long>::min() && u <= std::numeric_limits<unsigned long>::max()) {
767               return const_var(static_cast<unsigned long>(u));
768             } else {
769               return const_var(static_cast<unsigned long long>(u));
770             }
771           } catch (const std::out_of_range &) {
772             // it's just simply too big
773             return const_var(std::numeric_limits<long long>::max());
774           }
775         }
776 
777 #ifdef __GNUC__
778 #pragma GCC diagnostic pop
779 #endif
780 
781       }
782 
783       template<typename T, typename ... Param>
make_node(std::string t_match,const int t_prev_line,const int t_prev_col,Param &&...param)784       std::unique_ptr<eval::AST_Node_Impl<Tracer>> make_node(std::string t_match, const int t_prev_line, const int t_prev_col, Param && ...param)
785       {
786         return chaiscript::make_unique<eval::AST_Node_Impl<Tracer>, T>(std::move(t_match), Parse_Location(m_filename, t_prev_line, t_prev_col, m_position.line, m_position.col), std::forward<Param>(param)...);
787       }
788 
789       /// Reads a number from the input, detecting if it's an integer or floating point
Num()790       bool Num() {
791         SkipWS();
792 
793         const auto start = m_position;
794         if (m_position.has_more() && char_in_alphabet(*m_position, detail::float_alphabet) ) {
795           try {
796             if (Hex_()) {
797               auto match = Position::str(start, m_position);
798               auto bv = buildInt(16, match, true);
799               m_match_stack.emplace_back(make_node<eval::Constant_AST_Node<Tracer>>(std::move(match), start.line, start.col, std::move(bv)));
800               return true;
801             }
802 
803             if (Binary_()) {
804               auto match = Position::str(start, m_position);
805               auto bv = buildInt(2, match, true);
806               m_match_stack.push_back(make_node<eval::Constant_AST_Node<Tracer>>(std::move(match), start.line, start.col, std::move(bv)));
807               return true;
808             }
809             if (Float_()) {
810               auto match = Position::str(start, m_position);
811               auto bv = buildFloat(match);
812               m_match_stack.push_back(make_node<eval::Constant_AST_Node<Tracer>>(std::move(match), start.line, start.col, std::move(bv)));
813               return true;
814             }
815             else {
816               IntSuffix_();
817               auto match = Position::str(start, m_position);
818               if (!match.empty() && (match[0] == '0')) {
819                 auto bv = buildInt(8, match, false);
820                 m_match_stack.push_back(make_node<eval::Constant_AST_Node<Tracer>>(std::move(match), start.line, start.col, std::move(bv)));
821               }
822               else if (!match.empty()) {
823                 auto bv = buildInt(10, match, false);
824                 m_match_stack.push_back(make_node<eval::Constant_AST_Node<Tracer>>(std::move(match), start.line, start.col, std::move(bv)));
825               } else {
826                 return false;
827               }
828               return true;
829             }
830           } catch (const std::invalid_argument &) {
831             // error parsing number passed in to buildFloat/buildInt
832             return false;
833           }
834         }
835         else {
836           return false;
837         }
838       }
839 
840       /// Reads an identifier from input which conforms to C's identifier naming conventions, without skipping initial whitespace
Id_()841       bool Id_() {
842         if (m_position.has_more() && char_in_alphabet(*m_position, detail::id_alphabet)) {
843           while (m_position.has_more() && char_in_alphabet(*m_position, detail::keyword_alphabet) ) {
844             ++m_position;
845           }
846 
847           return true;
848         } else if (m_position.has_more() && (*m_position == '`')) {
849           ++m_position;
850           const auto start = m_position;
851 
852           while (m_position.has_more() && (*m_position != '`')) {
853             if (Eol()) {
854               throw exception::eval_error("Carriage return in identifier literal", File_Position(m_position.line, m_position.col), *m_filename);
855             }
856             else {
857               ++m_position;
858             }
859           }
860 
861           if (start == m_position) {
862             throw exception::eval_error("Missing contents of identifier literal", File_Position(m_position.line, m_position.col), *m_filename);
863           }
864           else if (!m_position.has_more()) {
865             throw exception::eval_error("Incomplete identifier literal", File_Position(m_position.line, m_position.col), *m_filename);
866           }
867 
868           ++m_position;
869 
870           return true;
871         }
872         return false;
873       }
874 
875       /// Reads (and potentially captures) an identifier from input
Id(const bool validate)876       bool Id(const bool validate) {
877         SkipWS();
878 
879         const auto start = m_position;
880         if (Id_()) {
881 
882           auto text = Position::str(start, m_position);
883           const auto text_hash = utility::fnv1a_32(text.c_str());
884 
885           if (validate) {
886             validate_object_name(text);
887           }
888 
889 #ifdef CHAISCRIPT_MSVC
890 #pragma warning(push)
891 #pragma warning(disable : 4307)
892 #endif
893 
894           switch (text_hash) {
895             case utility::fnv1a_32("true"): {
896               m_match_stack.push_back(make_node<eval::Constant_AST_Node<Tracer>>(std::move(text), start.line, start.col, const_var(true)));
897             } break;
898             case utility::fnv1a_32("false"): {
899               m_match_stack.push_back(make_node<eval::Constant_AST_Node<Tracer>>(std::move(text), start.line, start.col, const_var(false)));
900             } break;
901             case utility::fnv1a_32("Infinity"): {
902               m_match_stack.push_back(make_node<eval::Constant_AST_Node<Tracer>>(std::move(text), start.line, start.col,
903                 const_var(std::numeric_limits<double>::infinity())));
904             } break;
905             case utility::fnv1a_32("NaN"): {
906               m_match_stack.push_back(make_node<eval::Constant_AST_Node<Tracer>>(std::move(text), start.line, start.col,
907                 const_var(std::numeric_limits<double>::quiet_NaN())));
908             } break;
909             case utility::fnv1a_32("__LINE__"): {
910               m_match_stack.push_back(make_node<eval::Constant_AST_Node<Tracer>>(std::move(text), start.line, start.col,
911                 const_var(start.line)));
912             } break;
913             case utility::fnv1a_32("__FILE__"): {
914               m_match_stack.push_back(make_node<eval::Constant_AST_Node<Tracer>>(std::move(text), start.line, start.col,
915                 const_var(m_filename)));
916             } break;
917             case utility::fnv1a_32("__FUNC__"): {
918               std::string fun_name = "NOT_IN_FUNCTION";
919               for (size_t idx = m_match_stack.empty() ? 0 : m_match_stack.size() - 1; idx > 0; --idx)
920               {
921                 if (m_match_stack[idx-1]->identifier == AST_Node_Type::Id
922                     && m_match_stack[idx-0]->identifier == AST_Node_Type::Arg_List) {
923                   fun_name = m_match_stack[idx-1]->text;
924                 }
925               }
926 
927               m_match_stack.push_back(make_node<eval::Constant_AST_Node<Tracer>>(std::move(text), start.line, start.col,
928                 const_var(fun_name)));
929             } break;
930             case utility::fnv1a_32("__CLASS__"): {
931               std::string fun_name = "NOT_IN_CLASS";
932               for (size_t idx = m_match_stack.empty() ? 0 : m_match_stack.size() - 1; idx > 1; --idx)
933               {
934                 if (m_match_stack[idx-2]->identifier == AST_Node_Type::Id
935                     && m_match_stack[idx-1]->identifier == AST_Node_Type::Id
936                     && m_match_stack[idx-0]->identifier == AST_Node_Type::Arg_List) {
937                   fun_name = m_match_stack[idx-2]->text;
938                 }
939               }
940 
941               m_match_stack.push_back(make_node<eval::Constant_AST_Node<Tracer>>(std::move(text), start.line, start.col,
942                 const_var(fun_name)));
943             } break;
944             case utility::fnv1a_32("_"): {
945               m_match_stack.push_back(make_node<eval::Constant_AST_Node<Tracer>>(std::move(text), start.line, start.col,
946                 Boxed_Value(std::make_shared<dispatch::Placeholder_Object>())));
947             } break;
948             default: {
949                 std::string val = std::move(text);
950               if (*start == '`') {
951                 // 'escaped' literal, like an operator name
952                 val = Position::str(start+1, m_position-1);
953               }
954               m_match_stack.push_back(make_node<eval::Id_AST_Node<Tracer>>(val, start.line, start.col));
955             } break;
956           }
957 
958 #ifdef CHAISCRIPT_MSVC
959 #pragma warning(pop)
960 #endif
961 
962 
963           return true;
964         } else {
965           return false;
966         }
967       }
968 
969       /// Reads an argument from input
Arg(const bool t_type_allowed=true)970       bool Arg(const bool t_type_allowed = true) {
971         const auto prev_stack_top = m_match_stack.size();
972         SkipWS();
973 
974         if (!Id(true)) {
975           return false;
976         }
977 
978         SkipWS();
979 
980         if (t_type_allowed) {
981           Id(true);
982         }
983 
984         build_match<eval::Arg_AST_Node<Tracer>>(prev_stack_top);
985 
986         return true;
987       }
988 
989 
990 
991       /// Reads a quoted string from input, without skipping initial whitespace
Quoted_String_()992       bool Quoted_String_() {
993         if (m_position.has_more() && (*m_position == '\"')) {
994           char prev_char = *m_position;
995           ++m_position;
996 
997           int in_interpolation = 0;
998           bool in_quote = false;
999 
1000           while (m_position.has_more() && ((*m_position != '\"') || (in_interpolation > 0) || (prev_char == '\\'))) {
1001 
1002             if (!Eol_()) {
1003               if (prev_char == '$' && *m_position == '{') {
1004                 ++in_interpolation;
1005               } else if (prev_char != '\\' && *m_position == '"') {
1006                 in_quote = !in_quote;
1007               } else if (*m_position == '}' && !in_quote) {
1008                 --in_interpolation;
1009               }
1010 
1011               if (prev_char == '\\') {
1012                 prev_char = 0;
1013               } else {
1014                 prev_char = *m_position;
1015               }
1016               ++m_position;
1017             }
1018           }
1019 
1020           if (m_position.has_more()) {
1021             ++m_position;
1022           } else {
1023             throw exception::eval_error("Unclosed quoted string", File_Position(m_position.line, m_position.col), *m_filename);
1024           }
1025 
1026           return true;
1027         }
1028         return false;
1029       }
1030 
1031       template<typename string_type>
1032       struct Char_Parser
1033       {
1034         string_type &match;
1035         typedef typename string_type::value_type char_type;
1036         bool is_escaped = false;
1037         bool is_interpolated = false;
1038         bool saw_interpolation_marker = false;
1039         bool is_octal = false;
1040         bool is_hex = false;
1041         bool is_unicode = false;
1042         const bool interpolation_allowed;
1043 
1044         string_type octal_matches;
1045         string_type hex_matches;
1046 
Char_Parserchaiscript::parser::ChaiScript_Parser::Char_Parser1047         Char_Parser(string_type &t_match, const bool t_interpolation_allowed)
1048           : match(t_match),
1049             interpolation_allowed(t_interpolation_allowed)
1050         {
1051         }
1052 
1053         Char_Parser &operator=(const Char_Parser &) = delete;
1054 
~Char_Parserchaiscript::parser::ChaiScript_Parser::Char_Parser1055         ~Char_Parser(){
1056           try {
1057             if (is_octal) {
1058               process_octal();
1059             }
1060 
1061             if (is_hex) {
1062               process_hex();
1063             }
1064 
1065             if (is_unicode) {
1066               process_unicode();
1067             }
1068           } catch (const std::invalid_argument &) {
1069             // escape sequence was invalid somehow, we'll pick this
1070             // up in the next part of parsing
1071           }
1072         }
1073 
process_hexchaiscript::parser::ChaiScript_Parser::Char_Parser1074         void process_hex()
1075         {
1076           if (!hex_matches.empty()) {
1077             auto val = stoll(hex_matches, nullptr, 16);
1078             match.push_back(char_type(val));
1079           }
1080           hex_matches.clear();
1081           is_escaped = false;
1082           is_hex = false;
1083         }
1084 
1085 
process_octalchaiscript::parser::ChaiScript_Parser::Char_Parser1086         void process_octal()
1087         {
1088           if (!octal_matches.empty()) {
1089             auto val = stoll(octal_matches, nullptr, 8);
1090             match.push_back(char_type(val));
1091           }
1092           octal_matches.clear();
1093           is_escaped = false;
1094           is_octal = false;
1095         }
1096 
1097 
process_unicodechaiscript::parser::ChaiScript_Parser::Char_Parser1098         void process_unicode()
1099         {
1100           if (!hex_matches.empty()) {
1101             auto val = stoll(hex_matches, nullptr, 16);
1102             hex_matches.clear();
1103             match += detail::Char_Parser_Helper<string_type>::str_from_ll(val);
1104           }
1105           is_escaped = false;
1106           is_unicode = false;
1107         }
1108 
parsechaiscript::parser::ChaiScript_Parser::Char_Parser1109         void parse(const char_type t_char, const int line, const int col, const std::string &filename) {
1110           const bool is_octal_char = t_char >= '0' && t_char <= '7';
1111 
1112           const bool is_hex_char  = (t_char >= '0' && t_char <= '9')
1113                                  || (t_char >= 'a' && t_char <= 'f')
1114                                  || (t_char >= 'A' && t_char <= 'F');
1115 
1116           if (is_octal) {
1117             if (is_octal_char) {
1118               octal_matches.push_back(t_char);
1119 
1120               if (octal_matches.size() == 3) {
1121                 process_octal();
1122               }
1123               return;
1124             } else {
1125               process_octal();
1126             }
1127           } else if (is_hex) {
1128             if (is_hex_char) {
1129               hex_matches.push_back(t_char);
1130 
1131               if (hex_matches.size() == 2*sizeof(char_type)) {
1132                 // This rule differs from the C/C++ standard, but ChaiScript
1133                 // does not offer the same workaround options, and having
1134                 // hexadecimal sequences longer than can fit into the char
1135                 // type is undefined behavior anyway.
1136                 process_hex();
1137               }
1138               return;
1139             } else {
1140               process_hex();
1141             }
1142           } else if (is_unicode) {
1143             if (is_hex_char) {
1144               hex_matches.push_back(t_char);
1145 
1146             if(hex_matches.size() == 4) {
1147               // Format is specified to be 'slash'uABCD
1148               // on collecting from A to D do parsing
1149               process_unicode();
1150             }
1151             return;
1152             } else {
1153               // Not a unicode anymore, try parsing any way
1154               // May be someone used 'slash'uAA only
1155               process_unicode();
1156             }
1157           }
1158 
1159           if (t_char == '\\') {
1160             if (is_escaped) {
1161               match.push_back('\\');
1162               is_escaped = false;
1163             } else {
1164               is_escaped = true;
1165             }
1166           } else {
1167             if (is_escaped) {
1168               if (is_octal_char) {
1169                 is_octal = true;
1170                 octal_matches.push_back(t_char);
1171               } else if (t_char == 'x') {
1172                 is_hex = true;
1173               } else if (t_char == 'u') {
1174                 is_unicode = true;
1175               } else {
1176                 switch (t_char) {
1177                   case ('\'') : match.push_back('\''); break;
1178                   case ('\"') : match.push_back('\"'); break;
1179                   case ('?') : match.push_back('?'); break;
1180                   case ('a') : match.push_back('\a'); break;
1181                   case ('b') : match.push_back('\b'); break;
1182                   case ('f') : match.push_back('\f'); break;
1183                   case ('n') : match.push_back('\n'); break;
1184                   case ('r') : match.push_back('\r'); break;
1185                   case ('t') : match.push_back('\t'); break;
1186                   case ('v') : match.push_back('\v'); break;
1187                   case ('$') : match.push_back('$'); break;
1188                   default: throw exception::eval_error("Unknown escaped sequence in string", File_Position(line, col), filename);
1189                 }
1190                 is_escaped = false;
1191               }
1192             } else if (interpolation_allowed && t_char == '$') {
1193               saw_interpolation_marker = true;
1194             } else {
1195               match.push_back(t_char);
1196             }
1197           }
1198         }
1199 
1200       };
1201 
1202 
1203       /// Reads (and potentially captures) a quoted string from input.  Translates escaped sequences.
Quoted_String()1204       bool Quoted_String() {
1205         SkipWS();
1206 
1207         const auto start = m_position;
1208 
1209         if (Quoted_String_()) {
1210           std::string match;
1211           const auto prev_stack_top = m_match_stack.size();
1212 
1213           bool is_interpolated = [&]()->bool {
1214             Char_Parser<std::string> cparser(match, true);
1215 
1216 
1217             auto s = start + 1, end = m_position - 1;
1218 
1219             while (s != end) {
1220               if (cparser.saw_interpolation_marker) {
1221                 if (*s == '{') {
1222                   //We've found an interpolation point
1223 
1224                   m_match_stack.push_back(make_node<eval::Constant_AST_Node<Tracer>>(match, start.line, start.col, const_var(match)));
1225 
1226                   if (cparser.is_interpolated) {
1227                     //If we've seen previous interpolation, add on instead of making a new one
1228                     build_match<eval::Binary_Operator_AST_Node<Tracer>>(prev_stack_top, "+");
1229                   }
1230 
1231                   //We've finished with the part of the string up to this point, so clear it
1232                   match.clear();
1233 
1234                   std::string eval_match;
1235 
1236                   ++s;
1237                   while ((s != end) && (*s != '}')) {
1238                     eval_match.push_back(*s);
1239                     ++s;
1240                   }
1241 
1242                   if (*s == '}') {
1243                     cparser.is_interpolated = true;
1244                     ++s;
1245 
1246                     const auto tostr_stack_top = m_match_stack.size();
1247 
1248                     m_match_stack.push_back(make_node<eval::Id_AST_Node<Tracer>>("to_string", start.line, start.col));
1249 
1250                     const auto ev_stack_top = m_match_stack.size();
1251 
1252                     try {
1253                       m_match_stack.push_back(parse_instr_eval(eval_match));
1254                     } catch (const exception::eval_error &e) {
1255                       throw exception::eval_error(e.what(), File_Position(start.line, start.col), *m_filename);
1256                     }
1257 
1258                     build_match<eval::Arg_List_AST_Node<Tracer>>(ev_stack_top);
1259                     build_match<eval::Fun_Call_AST_Node<Tracer>>(tostr_stack_top);
1260                     build_match<eval::Binary_Operator_AST_Node<Tracer>>(prev_stack_top, "+");
1261                   } else {
1262                     throw exception::eval_error("Unclosed in-string eval", File_Position(start.line, start.col), *m_filename);
1263                   }
1264                 } else {
1265                   match.push_back('$');
1266                 }
1267                 cparser.saw_interpolation_marker = false;
1268               } else {
1269                 cparser.parse(*s, start.line, start.col, *m_filename);
1270 
1271                 ++s;
1272               }
1273             }
1274 
1275             return cparser.is_interpolated;
1276           }();
1277 
1278           m_match_stack.push_back(make_node<eval::Constant_AST_Node<Tracer>>(match, start.line, start.col, const_var(match)));
1279 
1280           if (is_interpolated) {
1281             build_match<eval::Binary_Operator_AST_Node<Tracer>>(prev_stack_top, "+");
1282           }
1283 
1284           return true;
1285         } else {
1286           return false;
1287         }
1288       }
1289 
1290       /// Reads a character group from input, without skipping initial whitespace
Single_Quoted_String_()1291       bool Single_Quoted_String_() {
1292         bool retval = false;
1293         if (m_position.has_more() && (*m_position == '\'')) {
1294           retval = true;
1295           char prev_char = *m_position;
1296           ++m_position;
1297 
1298           while (m_position.has_more() && ((*m_position != '\'') || (prev_char == '\\'))) {
1299             if (!Eol_()) {
1300               if (prev_char == '\\') {
1301                 prev_char = 0;
1302               } else {
1303                 prev_char = *m_position;
1304               }
1305               ++m_position;
1306             }
1307           }
1308 
1309           if (m_position.has_more()) {
1310             ++m_position;
1311           } else {
1312             throw exception::eval_error("Unclosed single-quoted string", File_Position(m_position.line, m_position.col), *m_filename);
1313           }
1314         }
1315         return retval;
1316       }
1317 
1318       /// Reads (and potentially captures) a char group from input.  Translates escaped sequences.
Single_Quoted_String()1319       bool Single_Quoted_String() {
1320         SkipWS();
1321 
1322         const auto start = m_position;
1323         if (Single_Quoted_String_()) {
1324           std::string match;
1325 
1326           {
1327             // scope for cparser destructor
1328             Char_Parser<std::string> cparser(match, false);
1329 
1330             for (auto s = start + 1, end = m_position - 1; s != end; ++s) {
1331               cparser.parse(*s, start.line, start.col, *m_filename);
1332             }
1333           }
1334 
1335           if (match.size() != 1) {
1336             throw exception::eval_error("Single-quoted strings must be 1 character long", File_Position(m_position.line, m_position.col), *m_filename);
1337           }
1338 
1339           m_match_stack.push_back(make_node<eval::Constant_AST_Node<Tracer>>(match, start.line, start.col, const_var(char(match.at(0)))));
1340           return true;
1341         }
1342         else {
1343           return false;
1344         }
1345       }
1346 
1347       /// Reads a char from input if it matches the parameter, without skipping initial whitespace
Char_(const char c)1348       bool Char_(const char c) {
1349         if (m_position.has_more() && (*m_position == c)) {
1350           ++m_position;
1351           return true;
1352         } else {
1353           return false;
1354         }
1355       }
1356 
1357       /// Reads (and potentially captures) a char from input if it matches the parameter
Char(const char t_c)1358       bool Char(const char t_c) {
1359         SkipWS();
1360         return Char_(t_c);
1361       }
1362 
1363       /// Reads a string from input if it matches the parameter, without skipping initial whitespace
Keyword_(const utility::Static_String & t_s)1364       bool Keyword_(const utility::Static_String &t_s) {
1365         const auto len = t_s.size();
1366         if (m_position.remaining() >= len) {
1367           auto tmp = m_position;
1368           for (size_t i = 0; tmp.has_more() && i < len; ++i) {
1369             if (*tmp != t_s.c_str()[i]) {
1370               return false;
1371             }
1372             ++tmp;
1373           }
1374           m_position = tmp;
1375           return true;
1376         }
1377 
1378         return false;
1379       }
1380 
1381       /// Reads (and potentially captures) a string from input if it matches the parameter
Keyword(const utility::Static_String & t_s)1382       bool Keyword(const utility::Static_String &t_s) {
1383         SkipWS();
1384         const auto start = m_position;
1385         bool retval = Keyword_(t_s);
1386         // ignore substring matches
1387         if ( retval && m_position.has_more() && char_in_alphabet(*m_position, detail::keyword_alphabet) ) {
1388           m_position = start;
1389           retval = false;
1390         }
1391 
1392         return retval;
1393       }
1394 
is_operator(const std::string & t_s) const1395       bool is_operator(const std::string &t_s) const {
1396         return std::any_of(m_operator_matches.begin(), m_operator_matches.end(),
1397             [t_s](const std::vector<utility::Static_String> &opers) {
1398               return std::any_of(opers.begin(), opers.end(),
1399                 [t_s](const utility::Static_String &s) {
1400                   return t_s == s.c_str();
1401                 });
1402             });
1403       }
1404 
1405       /// Reads (and potentially captures) a symbol group from input if it matches the parameter
Symbol(const utility::Static_String & t_s,const bool t_disallow_prevention=false)1406       bool Symbol(const utility::Static_String &t_s, const bool t_disallow_prevention=false) {
1407         SkipWS();
1408         const auto start = m_position;
1409         bool retval = Symbol_(t_s);
1410 
1411         // ignore substring matches
1412         if (retval && m_position.has_more() && (t_disallow_prevention == false) && char_in_alphabet(*m_position,detail::symbol_alphabet)) {
1413           if (*m_position != '=' && is_operator(Position::str(start, m_position)) && !is_operator(Position::str(start, m_position+1))) {
1414             // don't throw this away, it's a good match and the next is not
1415           } else {
1416             m_position = start;
1417             retval = false;
1418           }
1419         }
1420 
1421         return retval;
1422       }
1423 
1424       /// Reads an end-of-line group from input, without skipping initial whitespace
Eol_(const bool t_eos=false)1425       bool Eol_(const bool t_eos = false) {
1426         bool retval = false;
1427 
1428         if (m_position.has_more() && (Symbol_(cr_lf()) || Char_('\n'))) {
1429           retval = true;
1430           //++m_position.line;
1431           m_position.col = 1;
1432         } else if (m_position.has_more() && !t_eos && Char_(';')) {
1433           retval = true;
1434         }
1435 
1436         return retval;
1437       }
1438 
1439       /// Reads until the end of the current statement
Eos()1440       bool Eos() {
1441         SkipWS();
1442 
1443         return Eol_(true);
1444       }
1445 
1446       /// Reads (and potentially captures) an end-of-line group from input
Eol()1447       bool Eol() {
1448         SkipWS();
1449 
1450         return Eol_();
1451       }
1452 
1453       /// Reads a comma-separated list of values from input. Id's only, no types allowed
Id_Arg_List()1454       bool Id_Arg_List() {
1455         SkipWS(true);
1456         bool retval = false;
1457 
1458         const auto prev_stack_top = m_match_stack.size();
1459 
1460         if (Arg(false)) {
1461           retval = true;
1462           while (Eol()) {}
1463 
1464           while (Char(',')) {
1465             while (Eol()) {}
1466             if (!Arg(false)) {
1467               throw exception::eval_error("Unexpected value in parameter list", File_Position(m_position.line, m_position.col), *m_filename);
1468             }
1469           }
1470         }
1471         build_match<eval::Arg_List_AST_Node<Tracer>>(prev_stack_top);
1472 
1473         SkipWS(true);
1474 
1475         return retval;
1476       }
1477 
1478       /// Reads a comma-separated list of values from input, for function declarations
Decl_Arg_List()1479       bool Decl_Arg_List() {
1480         SkipWS(true);
1481         bool retval = false;
1482 
1483         const auto prev_stack_top = m_match_stack.size();
1484 
1485         if (Arg()) {
1486           retval = true;
1487           while (Eol()) {}
1488 
1489           while (Char(',')) {
1490             while (Eol()) {}
1491             if (!Arg()) {
1492               throw exception::eval_error("Unexpected value in parameter list", File_Position(m_position.line, m_position.col), *m_filename);
1493             }
1494           }
1495         }
1496         build_match<eval::Arg_List_AST_Node<Tracer>>(prev_stack_top);
1497 
1498         SkipWS(true);
1499 
1500         return retval;
1501       }
1502 
1503 
1504       /// Reads a comma-separated list of values from input
Arg_List()1505       bool Arg_List() {
1506         SkipWS(true);
1507         bool retval = false;
1508 
1509         const auto prev_stack_top = m_match_stack.size();
1510 
1511         if (Equation()) {
1512           retval = true;
1513           while (Eol()) {}
1514           while (Char(',')) {
1515             while (Eol()) {}
1516             if (!Equation()) {
1517               throw exception::eval_error("Unexpected value in parameter list", File_Position(m_position.line, m_position.col), *m_filename);
1518             }
1519           }
1520         }
1521 
1522         build_match<eval::Arg_List_AST_Node<Tracer>>(prev_stack_top);
1523 
1524         SkipWS(true);
1525 
1526         return retval;
1527       }
1528 
1529       /// Reads possible special container values, including ranges and map_pairs
Container_Arg_List()1530       bool Container_Arg_List() {
1531         bool retval = false;
1532         SkipWS(true);
1533 
1534         const auto prev_stack_top = m_match_stack.size();
1535 
1536         if (Value_Range()) {
1537           retval = true;
1538           build_match<eval::Arg_List_AST_Node<Tracer>>(prev_stack_top);
1539         } else if (Map_Pair()) {
1540           retval = true;
1541           while (Eol()) {}
1542           while (Char(',')) {
1543             while (Eol()) {}
1544             if (!Map_Pair()) {
1545               throw exception::eval_error("Unexpected value in container", File_Position(m_position.line, m_position.col), *m_filename);
1546             }
1547           }
1548           build_match<eval::Arg_List_AST_Node<Tracer>>(prev_stack_top);
1549         } else if (Operator()) {
1550           retval = true;
1551           while (Eol()) {}
1552           while (Char(',')) {
1553             while (Eol()) {}
1554             if (!Operator()) {
1555               throw exception::eval_error("Unexpected value in container", File_Position(m_position.line, m_position.col), *m_filename);
1556             }
1557           }
1558           build_match<eval::Arg_List_AST_Node<Tracer>>(prev_stack_top);
1559         }
1560 
1561         SkipWS(true);
1562 
1563         return retval;
1564       }
1565 
1566       /// Reads a lambda (anonymous function) from input
Lambda()1567       bool Lambda() {
1568         bool retval = false;
1569 
1570         const auto prev_stack_top = m_match_stack.size();
1571 
1572         if (Keyword("fun")) {
1573           retval = true;
1574 
1575           if (Char('[')) {
1576             Id_Arg_List();
1577             if (!Char(']')) {
1578               throw exception::eval_error("Incomplete anonymous function bind", File_Position(m_position.line, m_position.col), *m_filename);
1579             }
1580           } else {
1581             // make sure we always have the same number of nodes
1582             build_match<eval::Arg_List_AST_Node<Tracer>>(prev_stack_top);
1583           }
1584 
1585           if (Char('(')) {
1586             Decl_Arg_List();
1587             if (!Char(')')) {
1588               throw exception::eval_error("Incomplete anonymous function", File_Position(m_position.line, m_position.col), *m_filename);
1589             }
1590           } else {
1591             throw exception::eval_error("Incomplete anonymous function", File_Position(m_position.line, m_position.col), *m_filename);
1592           }
1593 
1594 
1595           while (Eol()) {}
1596 
1597           if (!Block()) {
1598             throw exception::eval_error("Incomplete anonymous function", File_Position(m_position.line, m_position.col), *m_filename);
1599           }
1600 
1601           build_match<eval::Lambda_AST_Node<Tracer>>(prev_stack_top);
1602         }
1603 
1604         return retval;
1605       }
1606 
1607       /// Reads a function definition from input
Def(const bool t_class_context=false,const std::string & t_class_name="")1608       bool Def(const bool t_class_context = false, const std::string &t_class_name = "") {
1609         bool retval = false;
1610 
1611         const auto prev_stack_top = m_match_stack.size();
1612 
1613         if (Keyword("def")) {
1614           retval = true;
1615 
1616           if (t_class_context) {
1617             m_match_stack.push_back(make_node<eval::Id_AST_Node<Tracer>>(t_class_name, m_position.line, m_position.col));
1618           }
1619 
1620           if (!Id(true)) {
1621             throw exception::eval_error("Missing function name in definition", File_Position(m_position.line, m_position.col), *m_filename);
1622           }
1623 
1624           bool is_method = false;
1625 
1626           if (Symbol("::")) {
1627             //We're now a method
1628             is_method = true;
1629 
1630             if (!Id(true)) {
1631               throw exception::eval_error("Missing method name in definition", File_Position(m_position.line, m_position.col), *m_filename);
1632             }
1633           }
1634 
1635           if (Char('(')) {
1636             Decl_Arg_List();
1637             if (!Char(')')) {
1638               throw exception::eval_error("Incomplete function definition", File_Position(m_position.line, m_position.col), *m_filename);
1639             }
1640           }
1641 
1642           while (Eos()) {}
1643 
1644           if (Char(':')) {
1645             if (!Operator()) {
1646               throw exception::eval_error("Missing guard expression for function", File_Position(m_position.line, m_position.col), *m_filename);
1647             }
1648           }
1649 
1650           while (Eol()) {}
1651           if (!Block()) {
1652             throw exception::eval_error("Incomplete function definition", File_Position(m_position.line, m_position.col), *m_filename);
1653           }
1654 
1655           if (is_method || t_class_context) {
1656             build_match<eval::Method_AST_Node<Tracer>>(prev_stack_top);
1657           } else {
1658             build_match<eval::Def_AST_Node<Tracer>>(prev_stack_top);
1659           }
1660 
1661         }
1662 
1663         return retval;
1664       }
1665 
1666       /// Reads a function definition from input
Try()1667       bool Try() {
1668         bool retval = false;
1669 
1670         const auto prev_stack_top = m_match_stack.size();
1671 
1672         if (Keyword("try")) {
1673           retval = true;
1674 
1675           while (Eol()) {}
1676 
1677           if (!Block()) {
1678             throw exception::eval_error("Incomplete 'try' block", File_Position(m_position.line, m_position.col), *m_filename);
1679           }
1680 
1681           bool has_matches = true;
1682           while (has_matches) {
1683             while (Eol()) {}
1684             has_matches = false;
1685             if (Keyword("catch")) {
1686               const auto catch_stack_top = m_match_stack.size();
1687               if (Char('(')) {
1688                 if (!(Arg() && Char(')'))) {
1689                   throw exception::eval_error("Incomplete 'catch' expression", File_Position(m_position.line, m_position.col), *m_filename);
1690                 }
1691                 if (Char(':')) {
1692                   if (!Operator()) {
1693                     throw exception::eval_error("Missing guard expression for catch", File_Position(m_position.line, m_position.col), *m_filename);
1694                   }
1695                 }
1696               }
1697 
1698               while (Eol()) {}
1699 
1700               if (!Block()) {
1701                 throw exception::eval_error("Incomplete 'catch' block", File_Position(m_position.line, m_position.col), *m_filename);
1702               }
1703               build_match<eval::Catch_AST_Node<Tracer>>(catch_stack_top);
1704               has_matches = true;
1705             }
1706           }
1707           while (Eol()) {}
1708           if (Keyword("finally")) {
1709             const auto finally_stack_top = m_match_stack.size();
1710 
1711             while (Eol()) {}
1712 
1713             if (!Block()) {
1714               throw exception::eval_error("Incomplete 'finally' block", File_Position(m_position.line, m_position.col), *m_filename);
1715             }
1716             build_match<eval::Finally_AST_Node<Tracer>>(finally_stack_top);
1717           }
1718 
1719           build_match<eval::Try_AST_Node<Tracer>>(prev_stack_top);
1720         }
1721 
1722         return retval;
1723       }
1724 
1725       /// Reads an if/else if/else block from input
If()1726       bool If() {
1727         bool retval = false;
1728 
1729         const auto prev_stack_top = m_match_stack.size();
1730 
1731         if (Keyword("if")) {
1732           retval = true;
1733 
1734           if (!Char('(')) {
1735             throw exception::eval_error("Incomplete 'if' expression", File_Position(m_position.line, m_position.col), *m_filename);
1736           }
1737 
1738           if (!Equation()) {
1739             throw exception::eval_error("Incomplete 'if' expression", File_Position(m_position.line, m_position.col), *m_filename);
1740           }
1741 
1742           const bool is_if_init = Eol() && Equation();
1743 
1744           if (!Char(')')) {
1745             throw exception::eval_error("Incomplete 'if' expression", File_Position(m_position.line, m_position.col), *m_filename);
1746           }
1747 
1748           while (Eol()) {}
1749 
1750           if (!Block()) {
1751             throw exception::eval_error("Incomplete 'if' block", File_Position(m_position.line, m_position.col), *m_filename);
1752           }
1753 
1754           bool has_matches = true;
1755           while (has_matches) {
1756             while (Eol()) {}
1757             has_matches = false;
1758             if (Keyword("else")) {
1759               if (If()) {
1760                 has_matches = true;
1761               } else {
1762                 while (Eol()) {}
1763 
1764                 if (!Block()) {
1765                   throw exception::eval_error("Incomplete 'else' block", File_Position(m_position.line, m_position.col), *m_filename);
1766                 }
1767                 has_matches = true;
1768               }
1769             }
1770           }
1771 
1772           const auto num_children = m_match_stack.size() - prev_stack_top;
1773 
1774           if ((is_if_init && num_children == 3)
1775               || (!is_if_init && num_children == 2)) {
1776             m_match_stack.push_back(chaiscript::make_unique<eval::AST_Node_Impl<Tracer>, eval::Noop_AST_Node<Tracer>>());
1777           }
1778 
1779           if (!is_if_init) {
1780             build_match<eval::If_AST_Node<Tracer>>(prev_stack_top);
1781           } else {
1782             build_match<eval::If_AST_Node<Tracer>>(prev_stack_top+1);
1783             build_match<eval::Block_AST_Node<Tracer>>(prev_stack_top);
1784           }
1785         }
1786 
1787         return retval;
1788       }
1789 
1790       /// Reads a class block from input
Class(const bool t_class_allowed)1791       bool Class(const bool t_class_allowed) {
1792         bool retval = false;
1793 
1794         size_t prev_stack_top = m_match_stack.size();
1795 
1796         if (Keyword("class")) {
1797           if (!t_class_allowed) {
1798             throw exception::eval_error("Class definitions only allowed at top scope", File_Position(m_position.line, m_position.col), *m_filename);
1799           }
1800 
1801           retval = true;
1802 
1803           if (!Id(true)) {
1804             throw exception::eval_error("Missing class name in definition", File_Position(m_position.line, m_position.col), *m_filename);
1805           }
1806 
1807           const auto class_name = m_match_stack.back()->text;
1808 
1809           while (Eol()) {}
1810 
1811           if (!Class_Block(class_name)) {
1812             throw exception::eval_error("Incomplete 'class' block", File_Position(m_position.line, m_position.col), *m_filename);
1813           }
1814 
1815           build_match<eval::Class_AST_Node<Tracer>>(prev_stack_top);
1816         }
1817 
1818         return retval;
1819       }
1820 
1821 
1822       /// Reads a while block from input
While()1823       bool While() {
1824         bool retval = false;
1825 
1826         const auto prev_stack_top = m_match_stack.size();
1827 
1828         if (Keyword("while")) {
1829           retval = true;
1830 
1831           if (!Char('(')) {
1832             throw exception::eval_error("Incomplete 'while' expression", File_Position(m_position.line, m_position.col), *m_filename);
1833           }
1834 
1835           if (!(Operator() && Char(')'))) {
1836             throw exception::eval_error("Incomplete 'while' expression", File_Position(m_position.line, m_position.col), *m_filename);
1837           }
1838 
1839           while (Eol()) {}
1840 
1841           if (!Block()) {
1842             throw exception::eval_error("Incomplete 'while' block", File_Position(m_position.line, m_position.col), *m_filename);
1843           }
1844 
1845           build_match<eval::While_AST_Node<Tracer>>(prev_stack_top);
1846         }
1847 
1848         return retval;
1849       }
1850 
1851       /// Reads the ranged `for` conditions from input
Range_Expression()1852       bool Range_Expression() {
1853         // the first element will have already been captured by the For_Guards() call that preceeds it
1854         return Char(':') && Equation();
1855       }
1856 
1857 
1858       /// Reads the C-style `for` conditions from input
For_Guards()1859       bool For_Guards() {
1860         if (!(Equation() && Eol()))
1861         {
1862           if (!Eol())
1863           {
1864             return false;
1865           } else {
1866             m_match_stack.push_back(chaiscript::make_unique<eval::AST_Node_Impl<Tracer>, eval::Noop_AST_Node<Tracer>>());
1867           }
1868         }
1869 
1870         if (!(Equation() && Eol()))
1871         {
1872           if (!Eol())
1873           {
1874             return false;
1875           } else {
1876             m_match_stack.push_back(chaiscript::make_unique<eval::AST_Node_Impl<Tracer>, eval::Constant_AST_Node<Tracer>>(Boxed_Value(true)));
1877           }
1878         }
1879 
1880         if (!Equation())
1881         {
1882           m_match_stack.push_back(chaiscript::make_unique<eval::AST_Node_Impl<Tracer>, eval::Noop_AST_Node<Tracer>>());
1883         }
1884 
1885         return true;
1886       }
1887 
1888 
1889       /// Reads a for block from input
For()1890       bool For() {
1891         bool retval = false;
1892 
1893         const auto prev_stack_top = m_match_stack.size();
1894 
1895         if (Keyword("for")) {
1896           retval = true;
1897 
1898           if (!Char('(')) {
1899             throw exception::eval_error("Incomplete 'for' expression", File_Position(m_position.line, m_position.col), *m_filename);
1900           }
1901 
1902           const bool classic_for = For_Guards() && Char(')');
1903           if (!classic_for && !(Range_Expression() && Char(')'))) {
1904             throw exception::eval_error("Incomplete 'for' expression", File_Position(m_position.line, m_position.col), *m_filename);
1905           }
1906 
1907           while (Eol()) {}
1908 
1909           if (!Block()) {
1910             throw exception::eval_error("Incomplete 'for' block", File_Position(m_position.line, m_position.col), *m_filename);
1911           }
1912 
1913           const auto num_children = m_match_stack.size() - prev_stack_top;
1914 
1915           if (classic_for) {
1916             if (num_children != 4) {
1917               throw exception::eval_error("Incomplete 'for' expression", File_Position(m_position.line, m_position.col), *m_filename);
1918             }
1919             build_match<eval::For_AST_Node<Tracer>>(prev_stack_top);
1920           } else {
1921             if (num_children != 3) {
1922               throw exception::eval_error("Incomplete ranged-for expression", File_Position(m_position.line, m_position.col), *m_filename);
1923             }
1924             build_match<eval::Ranged_For_AST_Node<Tracer>>(prev_stack_top);
1925           }
1926         }
1927 
1928         return retval;
1929       }
1930 
1931 
1932       /// Reads a case block from input
Case()1933       bool Case() {
1934         bool retval = false;
1935 
1936         const auto prev_stack_top = m_match_stack.size();
1937 
1938         if (Keyword("case")) {
1939           retval = true;
1940 
1941           if (!Char('(')) {
1942             throw exception::eval_error("Incomplete 'case' expression", File_Position(m_position.line, m_position.col), *m_filename);
1943           }
1944 
1945           if (!(Operator() && Char(')'))) {
1946             throw exception::eval_error("Incomplete 'case' expression", File_Position(m_position.line, m_position.col), *m_filename);
1947           }
1948 
1949           while (Eol()) {}
1950 
1951           if (!Block()) {
1952             throw exception::eval_error("Incomplete 'case' block", File_Position(m_position.line, m_position.col), *m_filename);
1953           }
1954 
1955           build_match<eval::Case_AST_Node<Tracer>>(prev_stack_top);
1956         } else if (Keyword("default")) {
1957           retval = true;
1958 
1959           while (Eol()) {}
1960 
1961           if (!Block()) {
1962             throw exception::eval_error("Incomplete 'default' block", File_Position(m_position.line, m_position.col), *m_filename);
1963           }
1964 
1965           build_match<eval::Default_AST_Node<Tracer>>(prev_stack_top);
1966         }
1967 
1968         return retval;
1969       }
1970 
1971 
1972       /// Reads a switch statement from input
Switch()1973       bool Switch() {
1974         const auto prev_stack_top = m_match_stack.size();
1975 
1976         if (Keyword("switch")) {
1977 
1978           if (!Char('(')) {
1979             throw exception::eval_error("Incomplete 'switch' expression", File_Position(m_position.line, m_position.col), *m_filename);
1980           }
1981 
1982           if (!(Operator() && Char(')'))) {
1983             throw exception::eval_error("Incomplete 'switch' expression", File_Position(m_position.line, m_position.col), *m_filename);
1984           }
1985 
1986           while (Eol()) {}
1987 
1988           if (Char('{')) {
1989             while (Eol()) {}
1990 
1991             while (Case()) {
1992               while (Eol()) { } // eat
1993             }
1994 
1995             while (Eol()) { } // eat
1996 
1997             if (!Char('}')) {
1998               throw exception::eval_error("Incomplete block", File_Position(m_position.line, m_position.col), *m_filename);
1999             }
2000           }
2001           else {
2002             throw exception::eval_error("Incomplete block", File_Position(m_position.line, m_position.col), *m_filename);
2003           }
2004 
2005           build_match<eval::Switch_AST_Node<Tracer>>(prev_stack_top);
2006           return true;
2007 
2008         } else {
2009           return false;
2010         }
2011 
2012       }
2013 
2014 
2015       /// Reads a curly-brace C-style class block from input
Class_Block(const std::string & t_class_name)2016       bool Class_Block(const std::string &t_class_name) {
2017         bool retval = false;
2018 
2019         const auto prev_stack_top = m_match_stack.size();
2020 
2021         if (Char('{')) {
2022           retval = true;
2023 
2024           Class_Statements(t_class_name);
2025           if (!Char('}')) {
2026             throw exception::eval_error("Incomplete class block", File_Position(m_position.line, m_position.col), *m_filename);
2027           }
2028 
2029           if (m_match_stack.size() == prev_stack_top) {
2030             m_match_stack.push_back(chaiscript::make_unique<eval::AST_Node_Impl<Tracer>, eval::Noop_AST_Node<Tracer>>());
2031           }
2032 
2033           build_match<eval::Block_AST_Node<Tracer>>(prev_stack_top);
2034         }
2035 
2036         return retval;
2037       }
2038 
2039       /// Reads a curly-brace C-style block from input
Block()2040       bool Block() {
2041         bool retval = false;
2042 
2043         const auto prev_stack_top = m_match_stack.size();
2044 
2045         if (Char('{')) {
2046           retval = true;
2047 
2048           Statements();
2049           if (!Char('}')) {
2050             throw exception::eval_error("Incomplete block", File_Position(m_position.line, m_position.col), *m_filename);
2051           }
2052 
2053           if (m_match_stack.size() == prev_stack_top) {
2054             m_match_stack.push_back(chaiscript::make_unique<eval::AST_Node_Impl<Tracer>, eval::Noop_AST_Node<Tracer>>());
2055           }
2056 
2057           build_match<eval::Block_AST_Node<Tracer>>(prev_stack_top);
2058         }
2059 
2060         return retval;
2061       }
2062 
2063       /// Reads a return statement from input
Return()2064       bool Return() {
2065         const auto prev_stack_top = m_match_stack.size();
2066 
2067         if (Keyword("return")) {
2068           Operator();
2069           build_match<eval::Return_AST_Node<Tracer>>(prev_stack_top);
2070           return true;
2071         } else {
2072           return false;
2073         }
2074       }
2075 
2076       /// Reads a break statement from input
Break()2077       bool Break() {
2078         const auto prev_stack_top = m_match_stack.size();
2079 
2080         if (Keyword("break")) {
2081           build_match<eval::Break_AST_Node<Tracer>>(prev_stack_top);
2082           return true;
2083         } else {
2084           return false;
2085         }
2086       }
2087 
2088       /// Reads a continue statement from input
Continue()2089       bool Continue() {
2090         const auto prev_stack_top = m_match_stack.size();
2091 
2092         if (Keyword("continue")) {
2093           build_match<eval::Continue_AST_Node<Tracer>>(prev_stack_top);
2094           return true;
2095         } else {
2096           return false;
2097         }
2098       }
2099 
2100       /// Reads a dot expression(member access), then proceeds to check if it's a function or array call
Dot_Fun_Array()2101       bool Dot_Fun_Array() {
2102         bool retval = false;
2103 
2104         const auto prev_stack_top = m_match_stack.size();
2105         if (Lambda() || Num() || Quoted_String() || Single_Quoted_String() ||
2106             Paren_Expression() || Inline_Container() || Id(false))
2107         {
2108           retval = true;
2109           bool has_more = true;
2110 
2111           while (has_more) {
2112             has_more = false;
2113 
2114             if (Char('(')) {
2115               has_more = true;
2116 
2117               Arg_List();
2118               if (!Char(')')) {
2119                 throw exception::eval_error("Incomplete function call", File_Position(m_position.line, m_position.col), *m_filename);
2120               }
2121 
2122               build_match<eval::Fun_Call_AST_Node<Tracer>>(prev_stack_top);
2123               /// \todo Work around for method calls until we have a better solution
2124               if (!m_match_stack.back()->children.empty()) {
2125                 if (m_match_stack.back()->children[0]->identifier == AST_Node_Type::Dot_Access) {
2126                   if (m_match_stack.empty()) { throw exception::eval_error("Incomplete dot access fun call", File_Position(m_position.line, m_position.col), *m_filename);
2127 }
2128                   if (m_match_stack.back()->children.empty()) { throw exception::eval_error("Incomplete dot access fun call", File_Position(m_position.line, m_position.col), *m_filename);
2129 }
2130                   auto dot_access = std::move(m_match_stack.back()->children[0]);
2131                   auto func_call = std::move(m_match_stack.back());
2132                   m_match_stack.pop_back();
2133                   func_call->children.erase(func_call->children.begin());
2134                   if (dot_access->children.empty()) { throw exception::eval_error("Incomplete dot access fun call", File_Position(m_position.line, m_position.col), *m_filename);
2135 }
2136                   func_call->children.insert(func_call->children.begin(), std::move(dot_access->children.back()));
2137                   dot_access->children.pop_back();
2138                   dot_access->children.push_back(std::move(func_call));
2139                   if (dot_access->children.size() != 2) { throw exception::eval_error("Incomplete dot access fun call", File_Position(m_position.line, m_position.col), *m_filename);
2140 }
2141                   m_match_stack.push_back(std::move(dot_access));
2142                 }
2143               }
2144             } else if (Char('[')) {
2145               has_more = true;
2146 
2147               if (!(Operator() && Char(']'))) {
2148                 throw exception::eval_error("Incomplete array access", File_Position(m_position.line, m_position.col), *m_filename);
2149               }
2150 
2151               build_match<eval::Array_Call_AST_Node<Tracer>>(prev_stack_top);
2152             }
2153             else if (Symbol(".")) {
2154               has_more = true;
2155               if (!(Id(true))) {
2156                 throw exception::eval_error("Incomplete dot access fun call", File_Position(m_position.line, m_position.col), *m_filename);
2157               }
2158 
2159               if ( std::distance(m_match_stack.begin() + static_cast<int>(prev_stack_top), m_match_stack.end()) != 2) {
2160                 throw exception::eval_error("Incomplete dot access fun call", File_Position(m_position.line, m_position.col), *m_filename);
2161               }
2162               build_match<eval::Dot_Access_AST_Node<Tracer>>(prev_stack_top);
2163             }
2164           }
2165         }
2166 
2167         return retval;
2168       }
2169 
2170       /// Reads a variable declaration from input
Var_Decl(const bool t_class_context=false,const std::string & t_class_name="")2171       bool Var_Decl(const bool t_class_context = false, const std::string &t_class_name = "") {
2172         bool retval = false;
2173 
2174         const auto prev_stack_top = m_match_stack.size();
2175 
2176         if (t_class_context && (Keyword("attr") || Keyword("auto") || Keyword("var"))) {
2177           retval = true;
2178 
2179           m_match_stack.push_back(make_node<eval::Id_AST_Node<Tracer>>(t_class_name, m_position.line, m_position.col));
2180 
2181           if (!Id(true)) {
2182             throw exception::eval_error("Incomplete attribute declaration", File_Position(m_position.line, m_position.col), *m_filename);
2183           }
2184 
2185           build_match<eval::Attr_Decl_AST_Node<Tracer>>(prev_stack_top);
2186         } else if (Keyword("auto") || Keyword("var") ) {
2187           retval = true;
2188 
2189           if (Reference()) {
2190             // we built a reference node - continue
2191           } else if (Id(true)) {
2192             build_match<eval::Var_Decl_AST_Node<Tracer>>(prev_stack_top);
2193           } else {
2194             throw exception::eval_error("Incomplete variable declaration", File_Position(m_position.line, m_position.col), *m_filename);
2195           }
2196 
2197         } else if (Keyword("global")) {
2198           retval = true;
2199 
2200           if (!(Reference() || Id(true))) {
2201             throw exception::eval_error("Incomplete global declaration", File_Position(m_position.line, m_position.col), *m_filename);
2202           }
2203 
2204           build_match<eval::Global_Decl_AST_Node<Tracer>>(prev_stack_top);
2205         } else if (Keyword("attr")) {
2206           retval = true;
2207 
2208           if (!Id(true)) {
2209             throw exception::eval_error("Incomplete attribute declaration", File_Position(m_position.line, m_position.col), *m_filename);
2210           }
2211           if (!Symbol("::")) {
2212             throw exception::eval_error("Incomplete attribute declaration", File_Position(m_position.line, m_position.col), *m_filename);
2213           }
2214           if (!Id(true)) {
2215             throw exception::eval_error("Missing attribute name in definition", File_Position(m_position.line, m_position.col), *m_filename);
2216           }
2217 
2218 
2219           build_match<eval::Attr_Decl_AST_Node<Tracer>>(prev_stack_top);
2220         }
2221 
2222         return retval;
2223       }
2224 
2225       /// Reads an expression surrounded by parentheses from input
Paren_Expression()2226       bool Paren_Expression() {
2227         if (Char('(')) {
2228           if (!Operator()) {
2229             throw exception::eval_error("Incomplete expression", File_Position(m_position.line, m_position.col), *m_filename);
2230           }
2231           if (!Char(')')) {
2232             throw exception::eval_error("Missing closing parenthesis ')'", File_Position(m_position.line, m_position.col), *m_filename);
2233           }
2234           return true;
2235         } else {
2236           return false;
2237         }
2238       }
2239 
2240       /// Reads, and identifies, a short-form container initialization from input
Inline_Container()2241       bool Inline_Container() {
2242         const auto prev_stack_top = m_match_stack.size();
2243 
2244         if (Char('[')) {
2245           Container_Arg_List();
2246 
2247           if (!Char(']')) {
2248             throw exception::eval_error("Missing closing square bracket ']' in container initializer", File_Position(m_position.line, m_position.col), *m_filename);
2249           }
2250           if ((prev_stack_top != m_match_stack.size()) && (!m_match_stack.back()->children.empty())) {
2251             if (m_match_stack.back()->children[0]->identifier == AST_Node_Type::Value_Range) {
2252               build_match<eval::Inline_Range_AST_Node<Tracer>>(prev_stack_top);
2253             }
2254             else if (m_match_stack.back()->children[0]->identifier == AST_Node_Type::Map_Pair) {
2255               build_match<eval::Inline_Map_AST_Node<Tracer>>(prev_stack_top);
2256             }
2257             else {
2258               build_match<eval::Inline_Array_AST_Node<Tracer>>(prev_stack_top);
2259             }
2260           }
2261           else {
2262             build_match<eval::Inline_Array_AST_Node<Tracer>>(prev_stack_top);
2263           }
2264 
2265           return true;
2266         } else {
2267           return false;
2268         }
2269       }
2270 
2271       /// Parses a variable specified with a & aka reference
Reference()2272       bool Reference() {
2273         const auto prev_stack_top = m_match_stack.size();
2274 
2275         if (Symbol("&")) {
2276           if (!Id(true)) {
2277             throw exception::eval_error("Incomplete '&' expression", File_Position(m_position.line, m_position.col), *m_filename);
2278           }
2279 
2280           build_match<eval::Reference_AST_Node<Tracer>>(prev_stack_top);
2281           return true;
2282         } else {
2283           return false;
2284         }
2285       }
2286 
2287       /// Reads a unary prefixed expression from input
Prefix()2288       bool Prefix() {
2289         const auto prev_stack_top = m_match_stack.size();
2290         using SS = utility::Static_String;
2291         constexpr const std::array<utility::Static_String, 6> prefix_opers{{
2292             SS{"++"},
2293             SS{"--"},
2294             SS{"-"},
2295             SS{"+"},
2296             SS{"!"},
2297             SS{"~"}
2298         }};
2299 
2300         for (const auto &oper : prefix_opers)
2301         {
2302           const bool is_char = oper.size() == 1;
2303           if ((is_char && Char(oper.c_str()[0])) || (!is_char && Symbol(oper)))
2304           {
2305             if (!Operator(m_operators.size()-1)) {
2306               throw exception::eval_error("Incomplete prefix '" + std::string(oper.c_str()) + "' expression", File_Position(m_position.line, m_position.col), *m_filename);
2307             }
2308 
2309             build_match<eval::Prefix_AST_Node<Tracer>>(prev_stack_top, oper.c_str());
2310             return true;
2311           }
2312         }
2313 
2314         return false;
2315       }
2316 
2317       /// Parses any of a group of 'value' style ast_node groups from input
Value()2318       bool Value() {
2319         return Var_Decl() || Dot_Fun_Array() || Prefix();
2320       }
2321 
Operator_Helper(const size_t t_precedence,std::string & oper)2322       bool Operator_Helper(const size_t t_precedence, std::string &oper) {
2323         for (auto & elem : m_operator_matches[t_precedence]) {
2324           if (Symbol(elem)) {
2325             oper = elem.c_str();
2326             return true;
2327           }
2328         }
2329         return false;
2330       }
2331 
Operator(const size_t t_precedence=0)2332       bool Operator(const size_t t_precedence = 0) {
2333         bool retval = false;
2334         const auto prev_stack_top = m_match_stack.size();
2335 
2336         if (m_operators[t_precedence] != Operator_Precidence::Prefix) {
2337           if (Operator(t_precedence+1)) {
2338             retval = true;
2339             std::string oper;
2340             while (Operator_Helper(t_precedence, oper)) {
2341               while (Eol()) {}
2342               if (!Operator(t_precedence+1)) {
2343                 throw exception::eval_error("Incomplete '" + oper + "' expression",
2344                     File_Position(m_position.line, m_position.col), *m_filename);
2345               }
2346 
2347               switch (m_operators[t_precedence]) {
2348                 case(Operator_Precidence::Ternary_Cond) :
2349                   if (Symbol(":")) {
2350                     if (!Operator(t_precedence+1)) {
2351                       throw exception::eval_error("Incomplete '" + oper + "' expression",
2352                           File_Position(m_position.line, m_position.col), *m_filename);
2353                     }
2354                     build_match<eval::If_AST_Node<Tracer>>(prev_stack_top);
2355                   }
2356                   else {
2357                     throw exception::eval_error("Incomplete '" + oper + "' expression",
2358                         File_Position(m_position.line, m_position.col), *m_filename);
2359                   }
2360                   break;
2361 
2362                 case(Operator_Precidence::Addition) :
2363                 case(Operator_Precidence::Multiplication) :
2364                 case(Operator_Precidence::Shift) :
2365                 case(Operator_Precidence::Equality) :
2366                 case(Operator_Precidence::Bitwise_And) :
2367                 case(Operator_Precidence::Bitwise_Xor) :
2368                 case(Operator_Precidence::Bitwise_Or) :
2369                 case(Operator_Precidence::Comparison) :
2370                   build_match<eval::Binary_Operator_AST_Node<Tracer>>(prev_stack_top, oper);
2371                   break;
2372 
2373                 case(Operator_Precidence::Logical_And) :
2374                   build_match<eval::Logical_And_AST_Node<Tracer>>(prev_stack_top, oper);
2375                   break;
2376                 case(Operator_Precidence::Logical_Or) :
2377                   build_match<eval::Logical_Or_AST_Node<Tracer>>(prev_stack_top, oper);
2378                   break;
2379                 case(Operator_Precidence::Prefix) :
2380                   assert(false); // cannot reach here because of if() statement at the top
2381                   break;
2382 
2383 //                default:
2384 //                  throw exception::eval_error("Internal error: unhandled ast_node", File_Position(m_position.line, m_position.col), *m_filename);
2385               }
2386             }
2387           }
2388         } else {
2389           return Value();
2390         }
2391 
2392         return retval;
2393       }
2394 
2395       /// Reads a pair of values used to create a map initialization from input
Map_Pair()2396       bool Map_Pair() {
2397         bool retval = false;
2398 
2399         const auto prev_stack_top = m_match_stack.size();
2400         const auto prev_pos = m_position;
2401 
2402         if (Operator()) {
2403           if (Symbol(":")) {
2404             retval = true;
2405             if (!Operator()) {
2406               throw exception::eval_error("Incomplete map pair", File_Position(m_position.line, m_position.col), *m_filename);
2407             }
2408 
2409             build_match<eval::Map_Pair_AST_Node<Tracer>>(prev_stack_top);
2410           }
2411           else {
2412             m_position = prev_pos;
2413             while (prev_stack_top != m_match_stack.size()) {
2414               m_match_stack.pop_back();
2415             }
2416           }
2417         }
2418 
2419         return retval;
2420       }
2421 
2422       /// Reads a pair of values used to create a range initialization from input
Value_Range()2423       bool Value_Range() {
2424         bool retval = false;
2425 
2426         const auto prev_stack_top = m_match_stack.size();
2427         const auto prev_pos = m_position;
2428 
2429         if (Operator()) {
2430           if (Symbol("..")) {
2431             retval = true;
2432             if (!Operator()) {
2433               throw exception::eval_error("Incomplete value range", File_Position(m_position.line, m_position.col), *m_filename);
2434             }
2435 
2436             build_match<eval::Value_Range_AST_Node<Tracer>>(prev_stack_top);
2437           }
2438           else {
2439             m_position = prev_pos;
2440             while (prev_stack_top != m_match_stack.size()) {
2441               m_match_stack.pop_back();
2442             }
2443           }
2444         }
2445 
2446         return retval;
2447       }
2448 
2449       /// Parses a string of binary equation operators
Equation()2450       bool Equation() {
2451         const auto prev_stack_top = m_match_stack.size();
2452 
2453         using SS = utility::Static_String;
2454 
2455         if (Operator()) {
2456           for (const auto &sym : {SS{"="}, SS{":="}, SS{"+="}, SS{"-="}, SS{"*="}, SS{"/="}, SS{"%="}, SS{"<<="}, SS{">>="}, SS{"&="}, SS{"^="}, SS{"|="}})
2457           {
2458             if (Symbol(sym, true)) {
2459               SkipWS(true);
2460               if (!Equation()) {
2461                 throw exception::eval_error("Incomplete equation", File_Position(m_position.line, m_position.col), *m_filename);
2462               }
2463 
2464               build_match<eval::Equation_AST_Node<Tracer>>(prev_stack_top, sym.c_str());
2465               return true;
2466             }
2467           }
2468           return true;
2469         }
2470 
2471         return false;
2472       }
2473 
2474       /// Parses statements allowed inside of a class block
Class_Statements(const std::string & t_class_name)2475       bool Class_Statements(const std::string &t_class_name) {
2476         bool retval = false;
2477 
2478         bool has_more = true;
2479         bool saw_eol = true;
2480 
2481         while (has_more) {
2482           const auto start = m_position;
2483           if (Def(true, t_class_name) || Var_Decl(true, t_class_name)) {
2484             if (!saw_eol) {
2485               throw exception::eval_error("Two function definitions missing line separator", File_Position(start.line, start.col), *m_filename);
2486             }
2487             has_more = true;
2488             retval = true;
2489             saw_eol = true;
2490           } else if (Eol()) {
2491             has_more = true;
2492             retval = true;
2493             saw_eol = true;
2494           } else {
2495             has_more = false;
2496           }
2497         }
2498 
2499         return retval;
2500       }
2501 
2502       /// Top level parser, starts parsing of all known parses
Statements(const bool t_class_allowed=false)2503       bool Statements(const bool t_class_allowed = false) {
2504         bool retval = false;
2505 
2506         bool has_more = true;
2507         bool saw_eol = true;
2508 
2509         while (has_more) {
2510           const auto start = m_position;
2511           if (Def() || Try() || If() || While() || Class(t_class_allowed) || For() || Switch()) {
2512             if (!saw_eol) {
2513               throw exception::eval_error("Two function definitions missing line separator", File_Position(start.line, start.col), *m_filename);
2514             }
2515             has_more = true;
2516             retval = true;
2517             saw_eol = true;
2518           }
2519           else if (Return() || Break() || Continue() || Equation()) {
2520             if (!saw_eol) {
2521               throw exception::eval_error("Two expressions missing line separator", File_Position(start.line, start.col), *m_filename);
2522             }
2523             has_more = true;
2524             retval = true;
2525             saw_eol = false;
2526           }
2527           else if (Block() || Eol()) {
2528             has_more = true;
2529             retval = true;
2530             saw_eol = true;
2531           }
2532           else {
2533             has_more = false;
2534           }
2535         }
2536 
2537         return retval;
2538       }
2539 
parse(const std::string & t_input,const std::string & t_fname)2540       AST_NodePtr parse(const std::string &t_input, const std::string &t_fname) override
2541       {
2542         ChaiScript_Parser<Tracer, Optimizer> parser(m_tracer, m_optimizer);
2543         return parser.parse_internal(t_input, t_fname);
2544       }
2545 
parse_instr_eval(const std::string & t_input)2546       eval::AST_Node_Impl_Ptr<Tracer> parse_instr_eval(const std::string &t_input)
2547       {
2548         auto last_position    = m_position;
2549         auto last_filename    = m_filename;
2550         auto last_match_stack = std::exchange(m_match_stack, decltype(m_match_stack){});
2551 
2552         auto retval = parse_internal(t_input, "instr eval");
2553 
2554         m_position = std::move(last_position);
2555         m_filename = std::move(last_filename);
2556         m_match_stack = std::move(last_match_stack);
2557 
2558         return eval::AST_Node_Impl_Ptr<Tracer>(dynamic_cast<eval::AST_Node_Impl<Tracer>*>(retval.release()));
2559       }
2560 
2561       /// Parses the given input string, tagging parsed ast_nodes with the given m_filename.
parse_internal(const std::string & t_input,std::string t_fname)2562       AST_NodePtr parse_internal(const std::string &t_input, std::string t_fname) {
2563         m_position = Position(t_input.begin(), t_input.end());
2564         m_filename = std::make_shared<std::string>(std::move(t_fname));
2565 
2566         if ((t_input.size() > 1) && (t_input[0] == '#') && (t_input[1] == '!')) {
2567           while (m_position.has_more() && (!Eol())) {
2568             ++m_position;
2569           }
2570         }
2571 
2572         if (Statements(true)) {
2573           if (m_position.has_more()) {
2574             throw exception::eval_error("Unparsed input", File_Position(m_position.line, m_position.col), *m_filename);
2575           } else {
2576             build_match<eval::File_AST_Node<Tracer>>(0);
2577           }
2578         } else {
2579           m_match_stack.push_back(chaiscript::make_unique<eval::AST_Node_Impl<Tracer>, eval::Noop_AST_Node<Tracer>>());
2580         }
2581 
2582         AST_NodePtr retval(std::move(m_match_stack.front()));
2583         m_match_stack.clear();
2584         return retval;
2585       }
2586     };
2587   }
2588 }
2589 
2590 #if defined(CHAISCRIPT_MSVC) && defined(CHAISCRIPT_PUSHED_MIN_MAX)
2591 #undef CHAISCRIPT_PUSHED_MIN_MAX
2592 #pragma pop_macro("min")
2593 #pragma pop_macro("max")
2594 #endif
2595 
2596 
2597 #endif /* CHAISCRIPT_PARSER_HPP_ */
2598 
2599