Сначала несколько замечаний о вашей грамматике:
- вы должны присвоить правилам уникальные метки для левой и правой сторон (
e1=atom ('*' e2=atom ...
);
- вы, вероятно, захотите создать отдельные токены
sqrt
и [
вместо 1 единственного sqrt[
, иначе ввод, такой как "sqrt [ 9 ]"
(пробел между sqrt
и [
), не будет обрабатываться должным образом ;
- унарный минус обычно имеет более низкий приоритет, чем возведение в степень.
rickythefox писал:
Кажется, что расположение правила квадратного корня sqrt среди атомщиков работает, но я почти уверен, что оно должно быть где-то в правиле экспоненты? Или это должно?
Нет, все в порядке: оно должно иметь самый высокий приоритет. Говоря о приоритетности, обычная таблица приоритетов (от низшей к высшей) в вашем случае будет:
- сложение и вычитание;
- умножение и деление;
- одинарный минус;
- экспоненцирование;
- выражения в скобках (включая вызовы функций, например
sqrt[...]
).
rickythefox писал:
Грамматика допускает полные выражения, такие как 2 * (3 + 4). Я хочу также разрешить неполные выражения, например, 2 * (3+. Будучи новичком в ANTLR, я понятия не имею, как этого добиться. Пожалуйста, укажите мне нужный документ или приведите пример.
Это сложно.
Я действительно вижу только один способ: внутри вашего правила статистики вы сначала заставляете анализатор смотреть вперед в потоке токенов, чтобы проверить, действительно ли равен и expr
впереди. Это можно сделать с помощью синтаксического предиката . Как только синтаксический анализатор уверен, что есть expr
, только тогда проанализируйте указанное выражение. Если нет expr
, попробуйте сопоставить NEWLINE
, а если нет NEWLINE
, просто используйте один токен, отличный от NEWLINE
(который должен быть частью неполное выражение!). (я опубликую небольшую демонстрацию ниже)
rickythefox писал:
Если я хочу расширить эту грамматику, чтобы фактически выполнить вычисления, могу ли я как-то использовать ее повторно или мне нужно скопировать и вставить?
Правила синтаксического анализатора ANTLR могут возвращать более одного объекта. Конечно, это не совсем так, поскольку методы Java (в сущности, это правило синтаксического анализатора) могут возвращать только один объект. Правило парсера возвращает объект, который содержит ссылки на несколько объектов. Так что вы могли бы сделать:
stat returns [String str, double num]
: ...
;
Демонстрация
Принимая во внимание все мои подсказки, небольшая рабочая демонстрация может выглядеть так:
grammar Expr;
parse returns [String str, double num]
@init{$str = "";}
: (stat
{
$str += $stat.str;
$num = $stat.num;
if(!Double.isNaN($num)) {
System.out.println($stat.text.trim() + " = " + $num);
}
})+
;
stat returns [String str, double num]
: (expr)=> expr NEWLINE {$str = "<math>" + $expr.str + "</math>"; $num = $expr.num;}
| NEWLINE {$str = ""; $num = Double.NaN;}
| ~NEWLINE {$str = ""; $num = Double.NaN; System.err.println("Ignoring: " + $text);}
;
expr returns [String str, double num]
: e1=multExpr {$str = $e1.str; $num = $e1.num;}
( '+' e2=multExpr {$str += "<mo>+</mo>" + $e2.str; $num += $e2.num;}
| '-' e2=multExpr {$str += "<mo>-</mo>" + $e2.str; $num -= $e2.num;}
)*
;
multExpr returns [String str, double num]
: e1=unaryExpr {$str = $e1.str; $num = $e1.num;}
( '*' e2=unaryExpr {$str += "<mo>*</mo>" + $e2.str; $num *= $e2.num;}
| '/' e2=unaryExpr {$str += "<mo>/</mo>" + $e2.str; $num /= $e2.num;}
)*
;
unaryExpr returns [String str, double num]
: '-' e=expExpr {$str = "<mo>-</mo>" + $e.str; $num = -1 * $e.num;}
| e=expExpr {$str = $e.str; $num = $e.num;}
;
expExpr returns [String str, double num]
: e1=atom {$str = $e1.str; $num = $e1.num;}
( '^' e2=atom {$str = "<msup><mrow>" + $str + "</mrow><mrow>" + $e2.str + "</mrow></msup>"; $num = Math.pow($num, $e2.num);}
)*
;
atom returns [String str, double num]
: INT {$str = "<mn>" + $INT.text + "</mn>"; $num = Double.valueOf($INT.text);}
| 'sqrt' '[' expr ']' {$str = "<msqrt><mrow>" + $expr.str + "</mrow></msqrt>"; $num = Math.sqrt($expr.num);}
| '(' expr ')' {$str = "<mo>(</mo>" + $expr.str + "<mo>)</mo>"; $num = $expr.num;}
;
INT : '0'..'9'+;
NEWLINE : '\r'? '\n';
WS : (' '|'\t')+ {skip();};
(обратите внимание, что (...)=>
- это так называемый синтаксический предикат )
Вы можете проверить синтаксический анализатор, сгенерированный из приведенной выше грамматики, следующим классом:
import org.antlr.runtime.*;
public class Main {
public static void main(String[] args) throws Exception {
String src =
"sqrt [ 9 ] \n" +
"1+2*3 \n" +
"2*(3+ \n" +
"2*(3+42)^2 \n";
ExprLexer lexer = new ExprLexer(new ANTLRStringStream(src));
ExprParser parser = new ExprParser(new CommonTokenStream(lexer));
ExprParser.parse_return returnValue = parser.parse();
String mathML = returnValue.str;
double eval = returnValue.num;
// ...
}
}
И если вы теперь запустите класс выше, вы увидите, что ввод
sqrt [ 9 ]
1+2*3
2*(3+
2*(3+42)^2
выдаст следующий вывод:
sqrt[9] = 3.0
1+2*3 = 7.0
Ignoring: 2
Ignoring: *
Ignoring: (
Ignoring: 3
Ignoring: +
2*(3+42)^2 = 4050.0