Обуздание жадности ANTLR4 (построение грамматики ANTLR4 для существующего DSL)

У меня уже есть DSL, и я хотел бы создать для него грамматику ANTLR4.

Вот пример этого DSL:

rule isC {
    true  when O_M in [5, 6, 17, 34]
    false in other cases
}

rule isContract {
    true  when O_C in ['XX','XY','YY']
    false in other cases
}

rule isFixed {
    true  when F3 ==~ '.*/.*/.*-F.*/.*'
    false in other cases
}

rule temp[1].future {
    false when O_OF in ['C','P']
    true  in other cases
}

rule temp[0].scale {
    10 when O_M == 5 && O_C in ['YX']
    1  in other cases 
}

Как DSL анализируется просто с использованием регулярных выражений, которые превратились в полный беспорядок - поэтому необходима грамматика.

Он работает следующим образом: он извлекает левую (до when) и правую части, и они оцениваются Groovy.

Я все еще хотел бы, чтобы Groovy оценил его, но организовал процесс синтаксического анализа с использованием грамматики. Итак, по сути, мне нужно извлечь эти левую и правую части, используя какие-то подстановочные знаки.

К сожалению, я не могу понять, как это сделать. Вот что у меня есть до сих пор:

grammar RuleDSL;

rules: basic_rule+ EOF;

basic_rule: 'rule' rule_name '{' condition_expr+ '}';

name: CHAR+;
list_index: '[' DIGIT+ ']';
name_expr: name list_index*;
rule_name: name_expr ('.' name_expr)*;

condition_expr: when_condition_expr | otherwise_condition_expr;

condition: .*?;
result: .*?;
when_condition_expr: result WHEN condition;

otherwise_condition_expr: result IN_OTHER_CASES;

WHEN: 'when';
IN_OTHER_CASES: 'in other cases';


DIGIT: '0'..'9';
CHAR: 'a'..'z' | 'A'..'Z';
SYMBOL: '?' | '!' | '&' | '.' | ',' | '(' | ')' | '[' | ']' | '\\' | '/' | '%' 
      | '*' | '-' | '+' | '=' | '<' | '>' | '_' | '|' | '"' | '\'' | '~';


// Whitespace and comments

WS: [ \t\r\n\u000C]+ -> skip;
COMMENT: '/*' .*? '*/' -> skip;

Эта грамматика "слишком" жадная, и обрабатывается только одно правило. Я имею в виду, если я слушаю синтаксический анализ с

@Override
public void enterBasic_rule(Basic_ruleContext ctx) {
    System.out.println("ENTERING RULE");
}

@Override
public void exitBasic_rule(Basic_ruleContext ctx) {
    System.out.println(ctx.getText());
    System.out.println("LEAVING RULE");
}

У меня есть следующее в качестве вывода

ENTERING RULE
-- tons of text
LEAVING RULE

Как я могу сделать его менее жадным, поэтому, если я проанализирую этот ввод, я получу 5 правил? Жадность исходит от condition и result, я полагаю.


ОБНОВЛЕНИЕ: оказалось, что пропускать пробелы было не лучшей идеей, поэтому через некоторое время я получил следующее: ссылка на суть

Спасибо 280Z28 за подсказку!


person Alexey Grigorev    schedule 18.07.2013    source источник


Ответы (1)


Вместо использования .*? в правилах синтаксического анализатора попробуйте использовать ~'}'*, чтобы гарантировать, что эти правила не будут пытаться читать дальше конца правила.

Кроме того, вы пропускаете пробелы в своем лексере, но используете CHAR+ и DIGIT+ в правилах парсера. Это означает, что следующие элементы эквивалентны:

  1. rule temp[1].future
  2. rule t e m p [ 1 ] . f u t u r e

Кроме того, вы сделали in other cases одним токеном вместо 3, поэтому следующие элементы не эквивалентны:

true  in other cases
true  in  other cases

Вероятно, вам следует начать с создания следующих правил лексера, а затем сделать правила CHAR и DIGIT правилами fragment:

ID : CHAR+;
INT : DIGIT+;
person Sam Harwell    schedule 18.07.2013
comment
Спасибо, это был тот толчок, который мне был нужен! Я обновлю ответ, чтобы включить грамматику, с которой я столкнулся. - person Alexey Grigorev; 18.07.2013