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