1 // Copyright (c) 2017-2020 Dr. Colin Hirsch and Daniel Frey
2 // Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
3 
4 // This is a small experiment with a grammar that can recover from errors.
5 //
6 // Triggered by https://github.com/taocpp/PEGTL/issues/55
7 //
8 // The grammar will recognise simple expressions terminated by semicolons.
9 // When an expression fails to parse, it skips to the next expression
10 // by looking for the terminator.
11 //
12 // Try: build/src/example/pegtl/recover '1+2*3;1+2*(3-)-4;-5;6/;7*(8+9)'
13 
14 #include <iostream>
15 #include <string>
16 
17 #include <tao/pegtl.hpp>
18 
19 using namespace TAO_PEGTL_NAMESPACE;
20 
21 // clang-format off
22 
23 template< typename T >
24 struct skipping : until< T > {};
25 
26 template< typename R, typename T >
27 struct recoverable : sor< try_catch< must< R >, T >, skipping< T > > {};
28 
29 struct expr_sum;
30 struct expr_identifier : identifier {};
31 struct expr_number : plus< digit > {};
32 struct expr_braced : if_must< one< '(' >, pad< expr_sum, space >, one< ')' > > {};
33 
34 struct expr_value : sor< expr_identifier, expr_number, expr_braced > {};
35 
36 struct expr_power : list< must< expr_value >, one< '^' >, space > {};
37 struct expr_prod : list_must< expr_power, one< '*', '/', '%' >, space > {};
38 struct expr_sum : list_must< expr_prod, one< '+', '-' >, space > {};
39 
40 struct term : sor< one< ';' >, eof > {};
41 struct expr : pad< expr_sum, space > {};
42 struct recoverable_expr : recoverable< expr, term > {};
43 
44 struct my_grammar : star< not_at< eof >, recoverable_expr > {};
45 
46 // clang-format on
47 
48 template< typename Rule >
49 struct my_action
50 {};
51 
52 template< typename T >
53 struct my_action< skipping< T > >
54 {
55    template< typename ActionInput >
applymy_action56    static void apply( const ActionInput& in, bool& error )
57    {
58       if( !error ) {
59          std::cout << in.position() << ": Invalid expression \"" << in.string() << "\"" << std::endl;
60       }
61       error = true;
62    }
63 };
64 
65 template< typename R >
66 struct found
67 {
68    template< typename ActionInput >
applyfound69    static void apply( const ActionInput& in, bool& error )
70    {
71       if( !error ) {
72          std::cout << in.position() << ": Found " << tao::demangle< R >() << ": \"" << in.string() << "\"" << std::endl;
73       }
74    }
75 };
76 
77 // clang-format off
78 // template<> struct my_action< expr_identifier > : found< expr_identifier > {};
79 // template<> struct my_action< expr_number > : found< expr_number > {};
80 // template<> struct my_action< expr_braced > : found< expr_braced > {};
81 // template<> struct my_action< expr_value > : found< expr_value > {};
82 // template<> struct my_action< expr_power > : found< expr_power > {};
83 // template<> struct my_action< expr_prod > : found< expr_prod > {};
84 // template<> struct my_action< expr_sum > : found< expr_sum > {};
85 template<> struct my_action< expr > : found< expr > {};
86 // clang-format on
87 
88 template<>
89 struct my_action< recoverable_expr >
90 {
91    template< typename ActionInput >
applymy_action92    static void apply( const ActionInput& /*unused*/, bool& error )
93    {
94       error = false;
95       std::cout << std::string( 79, '-' ) << std::endl;
96    }
97 };
98 
99 template< typename Rule >
100 struct my_control
101    : normal< Rule >
102 {
103    template< typename ParseInput, typename... States >
raisemy_control104    [[noreturn]] static void raise( const ParseInput& in, States&&... st )
105    {
106       std::cout << in.position() << ": Parse error matching " << tao::demangle< Rule >() << std::endl;
107       normal< Rule >::raise( in, st... );
108    }
109 };
110 
main(int argc,char ** argv)111 int main( int argc, char** argv )
112 {
113    for( int i = 1; i < argc; ++i ) {
114       argv_input in( argv, i );
115       bool error = false;
116       parse< my_grammar, my_action, my_control >( in, error );
117    }
118    return 0;
119 }
120