/**
 * Alee Forth: A portable and concise Forth implementation in modern C++.
 * Copyright (C) 2023  Clyne Sullivan <clyne@bitgloo.com>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

#ifndef ALEEFORTH_PARSER_HPP
#define ALEEFORTH_PARSER_HPP

#include "executor.hpp"

class Parser
{
public:
    ParseStatus parse(State& state, std::string_view& str) {
        const auto end = str.find_first_of(" \t\n\r");
        const auto sub = str.substr(0, end);
        if (sub.empty())
            return ParseStatus::Finished;

        if (state.pass != Pass::None) {
            switch (state.pass) {
            case Pass::Comment:
                if (str.front() == ')')
                    state.pass = Pass::None;

                str = str.substr(1);
                break;
            case Pass::Colon:
                state.pass = Pass::None;
                    state.compiling = true;
                state.dict.addDefinition(sub);
                break;
            case Pass::Constant:
                state.pass = Pass::None;
                state.compiling = true;
                state.dict.addDefinition(sub);
                state.dict.add(CoreWords::HiddenWordLiteral);
                state.dict.add(state.pop());
                state.dict.add(CoreWords::findi(";"));
                CoreWords::run(CoreWords::findi(";"), state);
                break;
            default:
                break;
            }
        } else {
            if (auto i = CoreWords::findi(sub); i >= 0) {
                if (state.compiling)
                    state.dict.add(i);
                if (!state.compiling || sub.front() == ';')
                    CoreWords::run(i, state);
            } else if (auto j = state.dict.find(sub); j > 0) {
                auto e = state.dict.getexec(j);

                if (state.compiling) {
                    if (state.dict.read(j) & CoreWords::Immediate) {
                        state.compiling = false;
                        Executor::fullexec(state, e);
                        state.compiling = true;
                    } else {
                        state.dict.add(CoreWords::HiddenWordJump);
                        state.dict.add(e);
                    }
                } else {
                    Executor::fullexec(state, e);
                }
            } else {
                char *p;
                const auto l = static_cast<Cell>(std::strtol(sub.data(), &p, 10));

                if (p != sub.data()) {
                    if (state.compiling) {
                        state.dict.add(CoreWords::HiddenWordLiteral);
                        state.dict.add(l);
                    } else {
                        state.push(l);
                    }
                } else {
                    return ParseStatus::Error;
                }
            }

            if (end == std::string_view::npos)
                return ParseStatus::Finished;
        }

        const auto next = str.find_first_not_of(" \t\n\r", end);

        if (next == std::string_view::npos) {
            return ParseStatus::Finished;
        } else {
            str = str.substr(next);
            return ParseStatus::Continue;
        }
    }
};

#endif // ALEEFORTH_PARSER_HPP