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