/* @file DSequenceSsm.cpp */

#include "DSequenceSsm.hpp"
#include "ssm/ISyntaxStateMachine_DSequenceSsm.hpp"
#include "DExpectExprSsm.hpp"
#include <xo/expression2/SequenceExpr.hpp>
#include <xo/alloc2/GCObject.hpp>

#ifdef NOT_YET
#include "expect_expr_xs.hpp"
#include "let1_xs.hpp"
#include "xo/expression/DefineExpr.hpp"
#include "xo/expression/pretty_expression.hpp"
#endif

namespace xo {
#ifdef NOT_YET
    using xo::scm::DDefineExpr;
#endif
    using xo::facet::typeseq;

    namespace scm {
        void
        DSequenceSsm::start(ParserStateMachine * p_psm)
        {
            DArena::Checkpoint ckp = p_psm->parser_alloc().checkpoint();

            p_psm->push_ssm(ckp, DSequenceSsm::make(p_psm->parser_alloc(),
                                                     p_psm->expr_alloc()));
            /* want to accept anything that starts an expression,
             * except that rightbrace '}' ends it
             */
            DExpectExprSsm::start(true /*allow_defs*/,
                                  true /*cxl_on_rightbrace*/,
                                  false /*!cxl_on_rightparen*/,
                                  p_psm);
        }

        obj<ASyntaxStateMachine,DSequenceSsm>
        DSequenceSsm::make(DArena & mm,
                           obj<AAllocator> expr_mm)
        {
            return obj<ASyntaxStateMachine,DSequenceSsm>(_make(mm, expr_mm));
        }

        DSequenceSsm *
        DSequenceSsm::_make(DArena & mm,
                            obj<AAllocator> expr_mm)
        {
            void * mem = mm.alloc(typeseq::id<DSequenceSsm>(),
                                  sizeof(DSequenceSsm));

            DSequenceExpr * seq_expr = DSequenceExpr::_make_empty(expr_mm);

            return new (mem) DSequenceSsm(seq_expr);
        }

        DSequenceSsm::DSequenceSsm(DSequenceExpr * seq_expr) : seq_expr_{seq_expr}
        {}

        syntaxstatetype
        DSequenceSsm::ssm_type() const noexcept
        {
            return syntaxstatetype::sequence;
        }

        std::string_view
        DSequenceSsm::get_expect_str() const noexcept
        {
            return "expr|semicolon|rightbrace";
        }

        void
        DSequenceSsm::on_token(const Token & tk,
                               ParserStateMachine * p_psm)
        {
            scope log(XO_DEBUG(p_psm->debug_flag()), xtag("tk", tk));

            switch (tk.tk_type()) {
            case tokentype::tk_rightbrace:
                this->on_rightbrace_token(tk, p_psm);
                return;
            case tokentype::tk_symbol:
            case tokentype::tk_def:
            case tokentype::tk_deftype:
            case tokentype::tk_if:
            case tokentype::tk_then:
            case tokentype::tk_else:
            case tokentype::tk_colon:
            case tokentype::tk_singleassign:
            case tokentype::tk_string:
            case tokentype::tk_f64:
            case tokentype::tk_i64:
            case tokentype::tk_bool:
            case tokentype::tk_semicolon:
            case tokentype::tk_invalid:
            case tokentype::tk_quote:
            case tokentype::tk_leftparen:
            case tokentype::tk_rightparen:
            case tokentype::tk_leftbracket:
            case tokentype::tk_rightbracket:
            case tokentype::tk_leftbrace:
            case tokentype::tk_leftangle:
            case tokentype::tk_rightangle:
            case tokentype::tk_cmple:
            case tokentype::tk_cmpge:
            case tokentype::tk_dot:
            case tokentype::tk_comma:
            case tokentype::tk_doublecolon:
            case tokentype::tk_assign:
            case tokentype::tk_yields:
            case tokentype::tk_plus:
            case tokentype::tk_minus:
            case tokentype::tk_star:
            case tokentype::tk_slash:
            case tokentype::tk_cmpeq:
            case tokentype::tk_cmpne:
            case tokentype::tk_nil:
            case tokentype::tk_type:
            case tokentype::tk_lambda:
            case tokentype::tk_let:
            case tokentype::tk_in:
            case tokentype::tk_end:
            case tokentype::N:
                break;
            }

            // default = illegal token error
            DSyntaxStateMachine<DSequenceSsm>::on_token(tk, p_psm);
        }

        void
        DSequenceSsm::on_rightbrace_token(const Token & tk,
                                          ParserStateMachine * p_psm)
        {
            (void)tk;

            /** rightbrace ends DSequenceSsm **/

            obj<AExpression,DSequenceExpr> expr(seq_expr_);

            p_psm->pop_ssm();

            /* make sequence from expressions seen at this level,
             * and report it to parent
             */
            p_psm->top_ssm().on_parsed_expression(expr, p_psm);
        }

        void
        DSequenceSsm::on_parsed_expression(obj<AExpression> expr,
                                           ParserStateMachine * p_psm)
        {
             scope log(XO_DEBUG(p_psm->debug_flag()));

             // TODO: switch to printable facet

             log && log(xtag("expr", expr));

#ifdef NOT_YET
            /* TODO: if expr is a DefineExpr,
             *       then need to rewrite...
             *
             *   ...prefix
             *   DefineExpr(lhs_name, rhs)
             *   rest...
             *
             * becomes:
             *
             *   /-- .outer_seq_expr_
             *   v
             *   Sequence(
             *     ...prefix,
             *
             *           /-- .inner_lm_expr_
             *           v
             *     Apply(Lambda(gen999,
             *                  [Variable(lhs_name, type(rhs))],
             *                             /-- .expr_v_
             *                             v
             *                  sequencify(rest...)),
             *           rhs))
             *
             * so amongst other things,
             * helpful to have nested seequence_xs that propagates '}'
             * instead of swallowing it.
             */
            bp<DefineExpr> def_expr = DefineExpr::from(expr);

            if (def_expr) {
                /** nested_start: control returns via
                 *   .on_expr(x)
                 *      with x something like:
                 *    Apply(Lambda(gensym(),
                 *                 [Variable(def_expr->lhs_name(),
                 *                           def_expr->valuetype())],
                 *                 body...))
                 *  followed immediately by
                 *   .on_rightbrace_token()
                 **/
                let1_xs::start(def_expr->lhs_name(),
                               def_expr->rhs(),
                               p_psm);
            } else {
                this->expr_v_.push_back(expr.promote());

                expect_expr_xs::start(true /*allow_defs*/,
                                      true /*cxl_on_rightbrace*/,
                                      p_psm);
            }
#endif

            this->seq_expr_->push_back(p_psm->expr_alloc(), expr);
        }

        void
        DSequenceSsm::on_parsed_expression_with_token(obj<AExpression> expr,
                                                      const Token & tk,
                                                      ParserStateMachine * p_psm)
        {
            scope log(XO_DEBUG(p_psm->debug_flag()));

            if (tk.tk_type() == tokentype::tk_semicolon) {
                // keep sequence on stack, consuming semicolon

                this->seq_expr_->push_back(p_psm->expr_alloc(),
                                           expr);
                return;
            } else if (tk.tk_type() == tokentype::tk_rightbrace) {
                // rightbrace ends sequence

                this->seq_expr_->push_back(p_psm->expr_alloc(), expr);
                this->on_rightbrace_token(tk, p_psm);
                return;
            }

            Super::on_parsed_expression_with_token(expr, tk, p_psm);
        }

#ifdef NOT_YET
        void
        sequence_xs::on_expr_with_semicolon(bp<Expression> expr,
                                            parserstatemachine * p_psm)
        {
            /* sequence continues until right brace */
            this->on_expr(expr, p_psm);
        }
#endif

        bool
        DSequenceSsm::pretty(const xo::print::ppindentinfo & ppii) const
        {
            return ppii.pps()->pretty_struct
                       (ppii,
                        "DSequenceSsm",
                        refrtag("seq_expr.size", seq_expr_->size()),
                        refrtag("expect", this->get_expect_str()));
        }

        void
        DSequenceSsm::visit_gco_children(VisitReason reason,
                                         obj<AGCObjectVisitor> gc) noexcept
        {
            gc.visit_child(reason, &seq_expr_);
        }

    } /*namespace scm*/
} /*namespace xo*/


/* end DSequenceSsm.cpp */
