Перевод кода ML на C ++

Я нашел здесь здесь алгоритм синтаксического анализа, однако он в ML, и я не слишком хорошо с ним знаком. Для лучшего понимания алгоритма я пытаюсь перевести его на императивный язык вроде C ++. Вот несколько вещей, в которых я не уверен или не совсем понимаю.

Вот заголовок для синтаксического анализа постфиксного выражения (AFAIK технически это не заголовок, а совпадение, но я не знаком с функциональными терминами):

parse_postfix(stack, (e, []), 
                    ipts as RATOR (irator as (_, _, POSTFIX)) :: ipts') =

Это означает, что ipts является заголовком списка ipts' и является оператором постфикса? Почему внутри (irator as...) еще одна спичка? Он удаляет его из списка или все равно продвигается вперед? Или ipts остается остатком в списке после удаления оператора irator?

Мне сложно это перевести. Вот что я кодировал до сих пор:

#include <iostream>
#include <map>
#include <stack>
#include <string>
#include <vector>

enum Assoc { Left, Right, Noassoc };
enum Fixity { Prefix, Infix, Postfix };

struct Oper {
    std::string Symbol;
    int Precedence;
    Fixity Fix;     // We can't represent bound types that way (INFIX <assoc>)
    Assoc Asc;      // so we just make it have the operator anyway

    Oper(std::string const& s, int p, Fixity f, Assoc a)
        : Symbol(s), Precedence(p), Fix(f), Asc(a) { }
};

// A regular AST representation
struct Expr { };
struct ConstExpr : public Expr {
    int Value;

    ConstExpr(int i) : Value(i) { }
};
struct UryExpr : public Expr {
    const Expr *Sub;
    Oper *OP;

    UryExpr(const Expr *s, Oper *o)
        : Sub(s), OP(o) { }
};
struct BinExpr : public Expr {
    const Expr *LHS, *RHS;
    Oper *OP;

    BinExpr(const Expr *l, const Expr *r, Oper *o)
        : LHS(l), RHS(r), OP(o) { }
};

bool noparens(Oper *inner, Oper *outer, Assoc side) {
    int pi = inner->Precedence, po = outer->Precedence;
    Fixity fi = inner->Fix, fo = outer->Fix;
    Assoc ai = inner->Asc, ao = outer->Asc;
    if (pi > po) return true;
    if (side == Left && fi == Postfix) return true;
    if (side == Left && fi == Infix && ai == Left) return (fo == Infix && ao == Left);
    if (side == Right && fi == Postfix) return true;
    if (side == Right && fi == Infix && ai == Right) return (fo == Infix && ao == Right);
    if (side == Noassoc) {
        if (fi == Infix && fo == Infix) return ai == ao;
        return fi == fo;
    }
    return false;
}

struct StackElem {
    Oper *infixop;
    const Expr *exp;
    std::vector<Oper*> prefixes;

    StackElem(Oper* i, const Expr* e, std::vector<Oper*> pref) 
        : infixop(i), exp(e), prefixes(pref) {}
};
std::map<std::string, Oper*> OperatorMap;
Oper *juxtarator = new Oper(" <juxtarator> ", 100, Infix, Left);
Oper *minrator = new Oper(" <minimal precedence operator> ", -1, Infix, Noassoc);
Oper *srator(std::stack<StackElem> const& st) { return (st.empty() ? minrator : st.top().infixop); }

Oper* get_op(std::string s) {
    auto it = OperatorMap.find(s);
    if (it == OperatorMap.end()) return nullptr;
    return it->second;
}

Expr* parse_postfix(const std::stack<StackElem> stack, const Expr* e, const std::vector<Oper*> prefixes, const std::vector<std::string> ipts);

Expr* parse_prefix(const std::stack<StackElem> stack, const std::vector<Oper*> prefixes, const std::vector<std::string> ipts) {
    if (!ipts.empty()) {
        std::string head = ipts[0];
        std::vector<std::string> tail(ipts.begin() + 1, ipts.end());

        Oper* op = get_op(head);
        if (!op) return parse_postfix(stack, new ConstExpr(std::atoi(head.c_str())), prefixes, tail);
        if (op->Fix == Prefix) {
            std::vector<Oper*> newprefix = prefixes;
            newprefix.push_back(op);
            return parse_prefix(stack, prefixes, tail);
        }
        else throw std::string("Lookahead is not a prefix operator");
    }
    else throw std::string("Premature EOF");
}

Expr* parse_postfix(const std::stack<StackElem> stack, const Expr* e, const std::vector<Oper*> prefixes, const std::vector<std::string> ipts)
{
    if (prefixes.empty() && !ipts.empty()) {
        std::string head = ipts[0];
        std::vector<std::string> tail(ipts.begin() + 1, ipts.end());

        Oper* irator = get_op(head);
        if (irator) {
            if (irator->Fix == Postfix) {
                if (noparens(srator(stack), irator, Left)) {
                    if (!stack.empty()) {
                        StackElem el = stack.top();
                        std::stack<StackElem> stack_tail = stack;
                        stack_tail.pop();
                        return parse_postfix(stack_tail, new BinExpr(el.exp, e, el.infixop), el.prefixes, ipts);
                    } 
                    else throw std::string("Impossible");
                }
                else if (noparens(irator, srator(stack), Right)) {
                    return parse_postfix(stack, new UryExpr(e, irator), std::vector<Oper*>(), tail);
                }
                else throw std::string("Non-associative");
            }
            else if (irator->Fix == Infix) {
                if (noparens(srator(stack), irator, Left)) {
                    if (!stack.empty()) {
                        StackElem el = stack.top();
                        std::stack<StackElem> stack_tail = stack;
                        stack_tail.pop();
                        return parse_postfix(stack_tail, new BinExpr(el.exp, e, el.infixop), el.prefixes, ipts);
                    }
                    else throw std::string("Impossible");
                }
                else if (noparens(irator, srator(stack), Right)) {
                    std::stack<StackElem> newstack = stack;
                    newstack.push(StackElem(irator, e, std::vector<Oper*>()));
                    return parse_prefix(newstack, std::vector<Oper*>(), tail);
                }
                else throw std::string("Non-associative");
            }
        }
    }
    else if (!prefixes.empty() && !ipts.empty()) {
        std::string head = ipts[0];
        std::vector<std::string> tail(ipts.begin() + 1, ipts.end());
        Oper* op = prefixes[0];
        std::vector<Oper*> newprefixes(prefixes.begin() + 1, prefixes.end());

        Oper* irator = get_op(head);
        if (irator) {
            if (irator->Fix == Postfix) {
                if (noparens(op, irator, Noassoc)) {
                    return parse_postfix(stack, new UryExpr(e, op), newprefixes, ipts);
                }
                else if (noparens(irator, op, Noassoc)) {
                    return parse_postfix(stack, new UryExpr(e, irator), prefixes, tail);
                }
                else throw std::string("Equal precedence!");
            }
            else if (irator->Fix == Infix) {
                if (noparens(op, irator, Noassoc)) {
                    parse_postfix(stack, new UryExpr(e, op), newprefixes, ipts);
                }
                else if (noparens(irator, op, Noassoc)) {
                    std::stack<StackElem> newstack = stack;
                    newstack.push(StackElem(irator, e, prefixes));
                    return parse_prefix(newstack, std::vector<Oper*>(), tail);
                }
                else throw std::string("Equal precedence!");
            }
        }
    }

    std::vector<std::string> nnip = ipts;
    nnip.insert(nnip.begin(), juxtarator->Symbol);
    return parse_postfix(stack, e, prefixes, nnip);
}

Expr* parse(std::vector<std::string> input) {
    return parse_prefix(std::stack<StackElem>(), std::vector<Oper*>(), input);
}

int main(void)
{
    OperatorMap.insert(std::make_pair(minrator->Symbol, minrator));
    OperatorMap.insert(std::make_pair(juxtarator->Symbol, juxtarator));
    OperatorMap.insert(std::make_pair("+", new Oper("+", 3, Infix, Left)));
    std::vector<std::string> tokens = { "2", "+", "3" };
    try {
        Expr* e = parse(tokens);
    }
    catch (std::string err) {
        std::cout << "Error: " << err << std::endl;
    }

    system("PAUSE");
    return 0;
}

Я надеюсь, что эта часть является corect с префиксом синтаксического анализа, но я не знаю, как насчет реализации функции parse_postfix.

Редактировать:

Теперь это попытка быть полной тестовой программой, но по какой-то причине она терпит неудачу, так как для ввода 2 + 3 (или даже только одного числа) запускается исключение (Преждевременный EOF).


person Peter Lenkefi    schedule 16.01.2017    source источник
comment
Подробно алгоритм описан в статье.   -  person molbdnilo    schedule 16.01.2017
comment
@molbdnilo Да, но было бы неплохо посмотреть на пример реализации.   -  person Peter Lenkefi    schedule 16.01.2017


Ответы (1)


parse_postfix(stack, (e, []),
              ipts as RATOR (irator as (_, _, POSTFIX)) :: ipts') = ...

Это означает, что ipts является заголовком списка ipts' и является оператором постфикса?

Не совсем. оператор соответствия as на самом деле связывает менее жестко, чем конструкторы шаблонов, такие как ::; добавляя правильные круглые скобки, ipts становится полным списком с RATOR ... в начале и ipts' (на один элемент короче) в конце:

parse_postfix(stack, (e, []),
              ipts as (RATOR (irator as (_, _, POSTFIX)) :: ipts')) = ...

Почему внутри (irator as...) другая спичка?

Здесь оператор соответствия as используется для двух разных целей:

  1. Шаблоны ipts as (... :: ipts') и irator as (_, _, POSTFIX) используются, чтобы гарантировать, что переменные ipts и irator охватывают элементы определенной подструктуры, поэтому в теле функции гарантируется, что ipts никогда не будет пустым и что irator всегда будет постфиксным rator (иначе это не parse_postfix работа).

  2. В качестве небольшого повышения производительности. Норман также мог написать, например,

    parse_postfix(stack, (e, []),
                  RATOR (text, prec, POSTFIX) :: ipts') = ...
    

    и впоследствии обращаться к RATOR (text, prec, POSTFIX) всякий раз, когда он обращается к irator и RATOR (text, prec, POSTFIX :: ipts' всякий раз, когда он обращается к ipts. Но это и дольше, и сложнее для чтения, и требует переконструирования значений, которые уже созданы в памяти при обращении к irator и ipts (т.е. меньше копирования).

    Вместо этого вспомогательная функция noparens, конструктор значения UNARY, исключение ParseError и т. Д. Предназначены для непосредственной обработки irator 3-кортежа для этого удобства.

Он удаляет его из списка или все равно продвигается вперед? Или будет ipts остаток списка после удаления оператора irator?

Иногда и почти. ipts' - это остаток от списка, когда irator был удален, а ipts - это полный список без удаленных элементов. В зависимости от того, упоминаются ли ipts или ipts' в if-then-else, элемент выталкивается или нет.

Я надеюсь, что эта часть является corect с префиксом синтаксического анализа, но я не знаю, как насчет реализации функции parse_postfix.

Я не могу сказать прямо сейчас. Но одно можно сказать наверняка: будет намного проще перевести эти функции, если вы будете придерживаться неизменяемых структур данных. Однако он не будет работать так быстро.

person Simon Shine    schedule 16.01.2017
comment
Очень хороший ответ, многое проясняет! Не могли бы вы взглянуть, если я выполню остальное? - person Peter Lenkefi; 17.01.2017
comment
Я отредактировал свой ответ, но программа не дает ожидаемого результата, поэтому я думаю, что все еще делаю что-то не так. - person Peter Lenkefi; 17.01.2017