ANTLR правильная грамматика для оператора if - PullRequest
0 голосов
/ 02 мая 2018

Я создал простую грамматику для языка, который я решил назвать SWL:

grammar swl;

program   : 'begin' statement+ 'end';

statement : assign | add | sub | mul | div | print | whilecond | ifcond ;
condition : expr ;
expr      : '(' expr ')' | expr 'and' expr | expr 'or' expr | 'not' expr | expr '=' expr | expr '>=' expr | expr '<=' expr | expr '>' expr | expr '<' expr | ID | NUMBER;

assign    : 'let' ID 'be' (NUMBER | ID) ;
print     : 'print' (NUMBER | ID) ;

add       : 'add' (NUMBER | ID) 'to' ID ;
sub       : 'sub' (NUMBER | ID) 'to' ID ;
mul       : 'mul' (NUMBER | ID) 'to' ID ;
div       : 'div' (NUMBER | ID) 'to' ID ;

whilecond : 'while (' condition ') do' statement+ 'stop' ;

ifcond    : 'if (' condition ') then' statement+ 'stop' | 'if (' condition ') then' statement+ 'else' statement+ 'stop' ;

ID        : [a-z]+ ;
NUMBER    : [0-9]+ ;
WS        : [ \n\t]+ -> skip;
ErrorChar : . ;

У меня немного проблемы с ifcond. Учитывая эту программу SWL:

begin
    if (not(a > 1) or (c <= b)) then
     mul 5 to k
    stop
end

Я могу получить вывод C ++:

#include <iostream>

using namespace std;

int main() {
    if ( !(a>1) || (c<=b)) {
    k *= 5;
    }
}

Это здорово! Кстати со следующим входом:

begin
    if (not(a > 1) or (c <= b)) then
     mul 5 to k
    else
     mul 15 to k
    stop
end

У меня есть такой вывод:

#include <iostream>

using namespace std;

int main() {
    if ( !(a>1) || (c<=b)) {
    k *= 5;
    k *= 15;
    }
}

Как видите, во втором примере отсутствует часть else statement. Что случилось? Должен ли я изменить грамматику ifcond и добавить другое правило / переменную? Это файл MyListner.cpp.

void fixString(string& cond) {

    size_t pos = cond.find("and", 0);
    if (pos != string::npos) { cond.replace(pos, 3, " && "); }

    pos = cond.find("or", 0);
    if (pos != string::npos) { cond.replace(pos, 2, " || "); }

    pos = cond.find("not", 0);
    if (pos != string::npos) { cond.replace(pos, 3, " !"); }

}

void MyListener::enterIfcond(swlParser::IfcondContext *ctx) {
    string cond = ctx->condition()->getText();
    fixString(cond);

    cout << string(indent, ' ') << "if (" << cond << ") {" << endl;
}

void MyListener::exitIfcond(swlParser::IfcondContext *ctx) {
    cout << string(indent, ' ') << "}" << endl;
}

Я подозреваю, что грамматика недостаточно хороша, и мне понадобится другая переменная для вызова части else. Я не знаю, как решить эту проблему, любая идея?

1 Ответ

0 голосов
/ 04 мая 2018

С ifcond все в порядке, но было бы проще, если бы вы добавили другую переменную перед остальной, чтобы вы могли использовать метод exit слушателя. Используйте это:

ifelsecond: ifcond | statement ;
ifcond    : 'if (' condition ') then' statement+ 'stop' | 'if (' condition ') then' ifelsecond+ 'else' statement+ 'stop' ;

Теперь вы можете легко редактировать MyListner.cpp следующим образом:

void MyListener::enterIfcond(swlParser::IfcondContext *ctx) {
    string cond = ctx->condition()->getText();
    fixString(cond);

    cout << "\n" << string(indent, ' ') << "if (" << cond << ") {" << endl;
}

void MyListener::exitIfcond(swlParser::IfcondContext *ctx) {
    cout << string(indent, ' ') << "}\n" << endl;
}

void MyListener::exitIfelsecond(swlParser::IfelsecondContext *ctx) {
    cout << string(indent, ' ') << "} else {" << endl;
}

Ваша грамматика хороша, но вы используете не очень необходимую продукцию, которая затрудняет написание кода слушателя. Поскольку вы используете stop, у вас нет проблемы dangling else .

Решение, которое я дал, может быть не лучшим, но оно работает, потому что грамматика не является неоднозначной из-за «ограниченных» утверждений, которые заключены внутри чего-либо (в вашем случае then и stop).

...