fxos/shell/parser.h

214 lines
5.6 KiB
C++

//---
// fxos-shell.parser: Command-line parser
//---
#ifndef FXOS_PARSER_H
#define FXOS_PARSER_H
#include <cstdint>
#include <string>
#include <stdexcept>
#include <initializer_list>
#include <vector>
#include <map>
#include <functional>
#include <variant>
#include <fxos/memory.h>
#include <fxos/vspace.h>
#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<std::string> 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<T> types, bool ignore_spaces=true);
using OptionHandler = std::function<void(std::string const &value)>;
/* 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<long, Range> 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<std::string, OptionHandler> m_options;
};
#endif /* FXOS_PARSER_H */