//
//  Lol Engine - Sandbox program
//
//  Copyright © 2005-2015 Sam Hocevar <sam@hocevar.net>
//
//  This program is free software. It comes without any warranty, to
//  the extent permitted by applicable law. You can redistribute it
//  and/or modify it under the terms of the Do What the Fuck You Want
//  to Public License, Version 2, as published by the WTFPL Task Force.
//  See http://www.wtfpl.net/ for more details.
//

#if HAVE_CONFIG_H
#   include "config.h"
#endif

#include <lol/engine.h>

#include "pegtl.hh"
namespace lol { namespace parser = pegtl; }

#include "axe.h"
namespace lol { using namespace axe; }

using namespace lol;

//
// Parser tools for a simple calculator grammar with + - * /
//

struct calculator
{
    void push(double d) { stack.Push(d); }
    void pop() { printf("%f\n", stack.Pop()); }

    void mul() { auto x = stack.Pop(); stack.Push(stack.Pop() * x); }
    void div() { auto x = stack.Pop(); stack.Push(stack.Pop() / x); }
    void add() { auto x = stack.Pop(); stack.Push(stack.Pop() + x); }
    void sub() { auto x = stack.Pop(); stack.Push(stack.Pop() - x); }

    array<double> stack;
};

static void parse_pegtl(std::string const &str);
static void parse_axe(std::string const &str);

int main()
{
    std::string const str("42+2*(1-1+2+3-4*5)");

    parse_axe(str);
    parse_pegtl(str);

    return EXIT_SUCCESS;
}

//
// PegTL -- a PEG parser
//

namespace pegtl_parser
{
    using namespace lol::parser;

    #define ACTION(name, code) \
        struct name : action_base<name> { \
            static void apply(std::string const &ctx, calculator &c) { \
                code \
            } \
        };

    ACTION( do_number, c.push(atof(ctx.c_str())); )
    ACTION( do_op,
        switch (ctx[0])
        {
            case '*': c.mul(); break;
            case '/': c.div(); break;
            case '+': c.add(); break;
            case '-': c.sub(); break;
        } )
    ACTION( do_success, c.pop(); )

    #undef ACTION

    // number <- <digit> +
    struct number : ifapply<plus<digit>, do_number> {};

    // term <- number | "(" expr ")"
    struct term : sor<number,
                      seq<one<'('>, struct expr, one<')'>>> {};

    // factor <- term ( "*" term | "/" term ) *
    struct factor : seq<term,
                        star<ifapply<sor<seq<one<'*'>, term>,
                                         seq<one<'/'>, term>>, do_op>>> {};

    // expr <- factor ( "+" factor | "-" factor ) *
    struct expr : seq<factor,
                      star<ifapply<sor<seq<one<'+'>, factor>,
                                       seq<one<'-'>, factor>>, do_op>>> {};

    // stmt <- expr <end>
    struct stmt : ifapply<seq<expr, eof>, do_success> {};
};

static void parse_pegtl(std::string const & str)
{
    calculator c;
    pegtl::basic_parse_string<pegtl_parser::stmt>(str, c);
}

//
// AXE -- a recursive descent parser (needs right-recursion)
//

template<typename IT>
static void parse_axe_helper(IT i1, IT i2)
{
    calculator c;
    double d;

    #define ACTION(code) e_ref([&](...) { code })

    r_rule<IT> number, term, factor, factor_tail, expr, expr_tail, stmt;

    // number ::= <double>
    number = r_double(d) >> ACTION( c.push(d); );

    // term ::= number | ( expr )
    term = number
         | '(' & expr & ')';

    // factor ::= term factor_tail
    // factor_tail ::= * term factor_tail
    //               | / term factor_tail
    //               | ɛ
    factor_tail = '*' & term >> ACTION( c.mul(); ) & factor_tail
                | '/' & term >> ACTION( c.div(); ) & factor_tail
                | r_empty();
    factor = term & factor_tail;

    // expr ::= factor expr_tail
    // expr_tail ::= + factor expr_tail
    //             | - factor expr_tail
    //             | ɛ
    expr_tail = '+' & factor >> ACTION( c.add(); ) & expr_tail
              | '-' & factor >> ACTION( c.sub(); ) & expr_tail
              | r_empty();
    expr = factor & expr_tail;

    // stmt ::= expr <end>
    //        | <fail>
    stmt = expr & r_end() >> ACTION( c.pop(); )
         | r_fail([](...) { printf("malformed expression\n"); });

    #undef ACTION

    // Evaluate expression
    stmt(i1, i2);
}

static void parse_axe(std::string const &str)
{
    return parse_axe_helper(str.begin(), str.end());
}