//--- // fxos-shell.parser: Command-line parser //--- #ifndef FXOS_PARSER_H #define FXOS_PARSER_H #include #include #include #include #include #include #include #include #include #include #include "session.h" /* Token values; just integers, but with conversion to and from string */ class T { public: enum TokenName: int8_t { END = 0, SPC = 1, SEPARATOR = 2, NUM = 3, SYMBOL = 4, SYSCALL = 5, OPTION = 6, STRING = 7, /* Other tokens using character literals: + - * / % ( ) $ : '.' for "..", '<' for "<<", '>' for ">>" */ }; /* Construction with no value and explicit value */ T() = default; constexpr T(int name): m_name((TokenName)name) {} constexpr T(TokenName name): m_name(name) {} /* Conversion to integer value, comparisons */ constexpr operator TokenName() const noexcept { return m_name; } /* Conversion to string */ std::string str() const; private: TokenName m_name; }; /* Range with start included and end excluded */ struct Range { long start; long end; }; /* Token with their data */ struct Token { /* Lexer-style attribute */ struct LexAttribute { long NUM; char *STRING; }; /* Attribute once in the parser */ struct Attribute { long NUM; std::string STRING; /* SYMBOL, OPTION, STRING */ }; /* Token type and value */ T type; Attribute value; /* Conversion to string */ std::string str() const; }; //--- // Lexer interface //--- /* Whether all files have finished executing */ bool lex_idle(); /* Path of file that provided the last token, "" if at top-level */ std::string lex_last_used_file(); /* Lex this input coming from the interactive command-line */ void lex_repl(std::string input); /* Lex the input coming from these files, sequentially */ void lex_include(std::vector files); /* For the parser: get one token from the current source */ Token lex_read(); //--- // Parser interface for commands //--- /* Parser class, used both for final command parsing and completion */ class Parser { public: Parser(bool complete); /* Start parsing, should be called just once after constructing */ void start(); /* Whether the end of a command has been reached */ bool at_end() const; /* Lookahead token */ Token lookahead() const; /* Require that command is finished */ void end(); /* Exhaust all input (usually after encountering an error) */ void exhaust(); /* Whether we are parsing for autocompletion. This is only available to allow appropriate decisions about printing vs. ignoring error messages. Do not change parsing rules based on this information! */ bool completing() const; /* Expect a token of this type, or of any of the listed types */ Token expect(T type, bool ignore_spaces=true); Token expect(std::initializer_list types, bool ignore_spaces=true); using OptionHandler = std::function; /* Specify an option to be accepted until the next SEPARATOR or END token (basically for the current command). The function will be called when the option is found during a call to accept_options(). */ void option(std::string name, OptionHandler callback); /* Read a symbol from the specified category; the category name is used to determine completion options on the command-line */ std::string symbol(std::string category=""); /* Literal string */ std::string str(); /* Read a numerical constant, or an expression. Expression uses the space to access the program counter and query symbol values */ long num(); long expr(VirtualSpace *space); /* Read a range; again $ and symbols are interpreted. If (before) and (after) are both specified, a single value will also be accepted, and the range [value-before, value+after) will be returned. */ Range range(VirtualSpace *space, long before=-1, long after=-1); /* Read an expression or a range */ std::variant expr_or_range(VirtualSpace *space); /* Read a memory region (allows both ranges and symbolic names) */ FxOS::MemoryRegion region(VirtualSpace *space, long before=-1, long after=-1); /* Read options, if any. Never fails */ void accept_options(); /* Skip separators, used when reading several commands at once */ void skip_separators(); /* Read everything until end of command (used when commands abort) */ void exhaust_until_separator(); /* Exhaust input until separators, used for debugging the lexer */ void dump_command(); //--- // Completion system //--- class SyntaxError: public std::invalid_argument { public: SyntaxError(char const *what): std::invalid_argument(what) {} }; /* A completion request is thrown by the parser when in completion mode to signal the expected next element */ struct CompletionRequest { CompletionRequest(std::string const &c, std::string const &v, VirtualSpace *s=nullptr): category(c), value(v), space(s) {} std::string category; std::string value; VirtualSpace *space; }; //--- // Internal stuff //--- private: /* Read a token into the lookahead and return the former*/ Token feed(bool ignore_spaces=true); /* Parsing rules for expressions */ long term(); long factor(); long atom(); /* true if we're completing a partial command, false if we're parsing a finished one */ bool m_complete; /* Lookahead token */ Token m_la; /* Virtual space for evaluation of symbols, can only be non-null during calls to expr() */ VirtualSpace *m_expr_space; /* Options (retained until next SEPARATOR or END) */ std::map m_options; }; #endif /* FXOS_PARSER_H */