namespace console {
namespace detail {
+#ifndef DOXYGEN
+
+ ///////////////////////////////////////////////////////////////////////////
+ // append_a
+
struct append_action
{
template <class T, class Value>
template <class T>
inline boost::spirit::ref_value_actor<T, append_action>
append_a(T & ref)
- {
- return boost::spirit::ref_value_actor<T, append_action>(ref);
- }
+ { return boost::spirit::ref_value_actor<T, append_action>(ref); }
template <class T, class Value>
inline boost::spirit::ref_const_ref_actor<T, Value, append_action>
append_a(T & ref, Value const & value)
- {
- return boost::spirit::ref_const_ref_actor<T, Value, append_action>(ref, value);
- }
+ { return boost::spirit::ref_const_ref_actor<T, Value, append_action>(ref, value); }
+
+ ///////////////////////////////////////////////////////////////////////////
+ // Grammar
template <class ParseDispatcher>
struct CommandGrammar : boost::spirit::grammar<CommandGrammar<ParseDispatcher> >
: public boost::spirit::grammar_def< boost::spirit::rule<Scanner>,
boost::spirit::rule<Scanner> >
{
- boost::spirit::rule<Scanner> command, path, argument, word, string, hexstring, token;
- boost::spirit::rule<Scanner> punctuation, hexbyte, balanced_tokens, simple_argument;
- boost::spirit::rule<Scanner> complex_argument, builtin, skip;
-
+ boost::spirit::rule<Scanner> command, path, argument, word, string, hexstring, token,
+ punctuation, hexbyte, balanced_tokens, simple_argument, complex_argument, builtin,
+ skip, commands, block, statement, relpath, abspath;
boost::spirit::chset<> special_p, punctuation_p, space_p, invalid_p, word_p;
-
boost::spirit::distinct_parser<> keyword_p;
definition(CommandGrammar const & self) :
// Characters with a special meaning within the parser
- // and characters reserved for future extensions
special_p ("/(){};"),
// Characters which are returned as punctuation tokens
- punctuation_p (",="),
+ // (only allowed within '()')
+ punctuation_p (",=/{};"),
- // Whitespace characters (we don't want newlines in there)
- space_p (" \t"),
+ // Whitespace characters
+ space_p (" \t\n\r"),
// Invalid characters: All chars below \x20 (space) which are not space_p
// (don't put a \0 in the chset<> argument *string* ...)
using namespace boost::spirit;
typedef ParseDispatcher PD;
+ ///////////////////////////////////////////////////////////////////
+ // Spirit grammar
+ //
+ // Syntax summary:
+ // This is EBNF with some minor tweaks to accommodate C++ syntax
+ //
+ // * and + like EBNF but they precede their argument
+ // >> is followed by
+ // ! optional
+ // a % b match any number of a's separated by b
+ // a - b match a but not b
+ //
+ // Beside this, we use some special parsers (ch_p, eps_p, confix_p, lex_escape_ch_p,
+ // keyword_p, comment_p) and directives (lexeme_d), however, the parser should be
+ // quite readable.
+ //
+ // ch_p match character
+ // eps_p always matches nothing (to attach unconditional actions)
+ // confix_p(a,b,c) match b, preceded by a and terminated by c. Used to parse
+ // string literals and comments
+ // lex_escape_ch_p match a lex style escape char. This is like a C++ style
+ // literal string escape char, however \x will be replaced by 'x'
+ // for any char 'x' if it has no special meaning.
+ // keyword_p match a delimited keyword
+ // comment_p(a,b) match comment starting with a and terminated with b. b
+ // defaults to end-of-line
+ //
+ // lexeme_d don't skip whitespace (as defined by the skip parser)
+ //
+ // Aligned to the right at column 50 are semantic actions.
+ //
+ // For clarity, I have used 'ch_p' explicitly throughout even though it is optional
+ // in most cases.
+ //
+ // More info is in the Boost.Spirit documentation
+
+ commands
+ = * command
+ ;
+
+ command
+ = builtin >> (ch_p(';') | end_p)
+ | path >> ( block | statement )
+ | ch_p(';') // Ignore empty commands
+ ;
+
builtin
= keyword_p("cd")
>> path
>> eps_p [ self.dispatch(&PD::builtin_cd,
boost::ref(self.context.path)) ]
- | keyword_p("ls")
+ | keyword_p("ls")
>> ! path
- >> eps_p [ self.dispatch(&PD::builtin_cd,
+ >> eps_p [ self.dispatch(&PD::builtin_ls,
+ boost::ref(self.context.path)) ]
+ | keyword_p("exit") [ self.dispatch(&PD::builtin_exit) ]
+
+ | keyword_p("help")
+ >> ! path
+ >> eps_p [ self.dispatch(&PD::builtin_help,
boost::ref(self.context.path)) ]
;
- command
- = builtin
- | path [ self.dispatch(&PD::beginCommand,
+ block
+ = ch_p('{') [ self.dispatch(&PD::pushDirectory,
+ boost::ref(self.context.path)) ]
+ >> * command
+ >> ch_p('}') [ self.dispatch(&PD::popDirectory) ]
+ ;
+
+ statement
+ = eps_p [ self.dispatch(&PD::beginCommand,
boost::ref(self.context.path)) ]
>> * argument
- >> ! ch_p(';')
+ >> (ch_p(';') | end_p)
>> eps_p [ self.dispatch(&PD::endCommand) ]
;
complex_argument // Argument consists of multiple tokens
= ch_p('(') [ self.dispatch(&PD::openGroup) ]
- >> * token
- >> ch_p(')') [ self.dispatch(&PD::closeGroup) ]
+ >> * token
+ >> ch_p(')') [ self.dispatch(&PD::closeGroup) ]
;
string // Returns value in context.str
path // Returns value in context.path
= eps_p [ clear_a(self.context.path) ]
- >> ( ! ch_p('/') [ push_back_a(self.context.path, "") ] )
- >> ( word [ push_back_a(self.context.path) ]
+ >> relpath | abspath
+ ;
+
+ relpath
+ = ( word [ push_back_a(self.context.path) ]
% ch_p('/') )
>> ( ! ch_p('/') [ push_back_a(self.context.path,"") ] )
;
+ abspath
+ = ch_p('/') [ push_back_a(self.context.path, "") ]
+ >> ( relpath
+ | eps_p [ push_back_a(self.context.path, "") ] )
+ ;
+
balanced_tokens
= ch_p('(') [ self.dispatch(&PD::pushPunctuation, "(") ]
>> * token
;
skip
- = space_p
- | comment_p('#')
+ = space_p | comment_p('#')
;
+ ///////////////////////////////////////////////////////////////////
+
+ start_parsers(
+ commands, // CommandParser
+ skip // SkipParser
+ );
+
BOOST_SPIRIT_DEBUG_TRACE_RULE(command,1);
BOOST_SPIRIT_DEBUG_TRACE_RULE(path,1);
BOOST_SPIRIT_DEBUG_TRACE_RULE(argument,1);
BOOST_SPIRIT_DEBUG_TRACE_RULE(simple_argument,1);
BOOST_SPIRIT_DEBUG_TRACE_RULE(complex_argument,1);
BOOST_SPIRIT_DEBUG_TRACE_RULE(builtin,1);
-
- start_parsers(
- command, // CommandParser
- skip // SkipParser
- );
-
+ BOOST_SPIRIT_DEBUG_TRACE_RULE(commands,1);
+ BOOST_SPIRIT_DEBUG_TRACE_RULE(block,1);
+ BOOST_SPIRIT_DEBUG_TRACE_RULE(statement,1);
+ BOOST_SPIRIT_DEBUG_TRACE_RULE(relpath,1);
+ BOOST_SPIRIT_DEBUG_TRACE_RULE(abspath,1);
}
};
};
+
+
+#endif
+
}}}
///////////////////////////////ih.e////////////////////////////////////////