xo-numeric/xo-reader/include/xo/reader/reader.hpp

136 lines
4.4 KiB
C++

/* file reader.hpp
*
* author: Roland Conybeare, Aug 2024
*/
#pragma once
#include "parser.hpp"
#include "reader_error.hpp"
#include "xo/expression/Expression.hpp"
#include "xo/expression/pretty_expression.hpp"
#include "xo/tokenizer/tokenizer.hpp"
namespace xo {
namespace scm {
/** @class parse_result
* @brief Result object returned from reader::read_expr
**/
struct reader_result {
using Expression = xo::ast::Expression;
using span_type = span<const char>;
reader_result(rp<Expression> expr, span_type rem, std::size_t psz, const reader_error & error)
: expr_{std::move(expr)}, rem_{rem}, parser_stack_size_{psz}, error_{error} {}
/** true if reader parsed a complete expression **/
bool expr_complete() const { return expr_.get(); }
/** parsed schematica expression **/
rp<Expression> expr_;
/** span giving text input consumed to construct expr,
* including any leading whitespace.
* This is the span returned in result of tokenizer<char>::scan()
**/
span_type rem_;
/** parser nesting level when this result delivered
* will be zero whenever @ref expr_ is non-null
**/
std::size_t parser_stack_size_ = 0;
/** error description, whenever .error_.is_error() is true **/
reader_error error_;
};
/**
* Use:
* @code
* reader rdr;
* rdr.begin_translation_unit()
*
* bool eof = false;
* while (!eof) {
* auto input = ins.read_some();
* // eof: true if no more input will be forthcoming from this stream
* eof = ins.eof();
*
* for (auto rem = input; !rem.empty();) {
* // res: (parsed-expr, used)
* auto [expres = rdr.read_expr(rem, eof);
*
* if (res.first) {
* // do something with res.first (parsed expr)
* ...
* }
*
* rem = rem.suffix_after(res.second);
* }
* }
*
* // expect !rdr.has_prefix()
*
* @endcode
**/
class reader {
public:
using tokenizer_type = tokenizer<char>;
using span_type = tokenizer_type::span_type;
public:
explicit reader(bool debug_flag);
/** call once before calling .read_expr()
* for a new interactive session
**/
void begin_interactive_session();
/** counterpart to .begin_interactive_session()
**/
reader_result end_interactive_session();
/** call once before calling .read_expr():
* 1. with new reader
* 2. if last read_expr() call had eof=true
**/
void begin_translation_unit();
/** counterpart to .begin_translation_unit(),
* provided for symmetry's sake
*
* Equivalent to:
* @code
* read_expr(span_type(nullptr, nullptr), true);
* @endcode
**/
reader_result end_translation_unit();
/** Try to read one expression from @p input.
* Return struct containing parsed expression
* and span of characters comprising that expression
*
* @param input Supply this input span of chars
* @param eof. True if input stream supplying @p input
* reports end-of-file immediately after the last char
* in @p input.
**/
reader_result read_expr(const span_type & input, bool eof);
/** reset to known starting point after encountering an error.
* - remainder of stashed current line.
* Necesary for well-formatted error reporting.
* - current parsing state
**/
void reset_to_idle_toplevel();
private:
/** tokenizer: text -> tokens **/
tokenizer_type tokenizer_;
/** parser: tokens -> expressions **/
parser parser_;
};
} /*namespace scm*/
} /*namespace xo*/
/* end reader.hpp */