/* @file reader.cpp */ #include "reader.hpp" namespace xo { namespace scm { reader::reader(const rp & toplevel_symtab, bool debug_flag) : tokenizer_{debug_flag}, parser_{toplevel_symtab, debug_flag} {} void reader::begin_interactive_session() { parser_.begin_interactive_session(); } reader_result reader::end_interactive_session() { return this->read_expr(span_type(nullptr, nullptr), true /*eof*/); } void reader::begin_translation_unit() { parser_.begin_translation_unit(); } reader_result reader::end_translation_unit() { return this->read_expr(span_type(nullptr, nullptr), true /*eof*/); } reader_result reader::read_expr(const span_type & input_arg, bool eof_flag) { scope log(XO_DEBUG(this->debug_flag())); span_type input = input_arg; /* input text-span consumed by this call. * Always comprises some number (possibly 0) * of complete tokens, along with any leading * whitespace. * * expr_span may also begin and end part way through * distinct input lines */ span_type expr_span = input.prefix(0ul); while (!input.empty()) { /* each loop iteration reads one token */ /* read one token from input. * tokenizer stashes one line at a time, but used_span only * reports in used_span the portion representing the first token. */ auto [tk, used_span, error1] = this->tokenizer_.scan(input, eof_flag); log && log(xtag("consumed", used_span)); log && log(xtag("input.pre", input)); expr_span += used_span; if (tk.is_valid()) { log && log("input_state.current_line", tokenizer_.input_state().current_line()); /* forward just-read token to parser */ auto parser_result = this->parser_.include_token(tk); log && log("after parser.include_token"); log && log("input_state.current_line", tokenizer_.input_state().current_line()); if (parser_result.is_expression()) { log && log(xtag("outcome", "victory!"), xtag("expr", parser_result.result_expr())); rp result_expr = parser_result.result_expr(); this->parser_.reset_result(); /* token completes an expression -> victory */ return reader_result(parser_result.result_expr(), expr_span, parser_.stack_size(), reader_error()); } else if (parser_result.is_error()) { /* 1. parser detected error. * 2. tokenizer_.input_state().current_pos refers to position just after offending token * 3. error_pos here is 0 because error detected at token boundary */ reader_error error2(parser_result.error_src_function(), parser_result.error_description(), tokenizer_.input_state().rewind(tk.text().size()), 0 /*error_pos*/); std::cout << "parser error pre-report:" << std::endl; error2.report(std::cout); return reader_result(nullptr, expr_span, parser_.stack_size(), error2); } else { /* token did not complete an expression * (e.g. token for '[') * * input span may contain more tokens -> iterate */ } } else /*invalid token*/ { if (error1.is_error()) { /* tokenizer detected an error */ std::cout << "tokenizer error pre-report:" << std::endl; error1.report(std::cout); return reader_result(nullptr, expr_span, parser_.stack_size(), reader_error(error1.src_function(), error1.error_description(), error1.input_state(), error1.error_pos())); } else if (used_span.size() > 0) { /* control here on reaching the last token in input line; * used_span will be the entire line */ } else { /* control should not come here: invalid token with no error */ assert(input.empty()); break; } } input = input.after_prefix(used_span); } /* control here: either * 1. input.empty (perhaps ate some whitespace, ok) * 2. missing or incomplete token (ok unless eof) */ if (eof_flag) { if (parser_.has_incomplete_expr()) { throw std::runtime_error ("reader::read_expr" ": eof reached with incomplete expression"); } if (tokenizer_.has_prefix()) { throw std::runtime_error ("reader::read_expr" ": unintelligible input recognized at eof"); } } log && log(xtag("outcome", "noop"), xtag("parser.stack_size", parser_.stack_size())); return reader_result(nullptr, expr_span, parser_.stack_size(), reader_error()); } void reader::reset_to_idle_toplevel() { this->tokenizer_.discard_current_line(); this->parser_.reset_to_idle_toplevel(); } } /*namespace scm*/ } /*namespace xo*/ /* end reader.cpp */