Java 如何使用ANTLR v3构建日期/小时语法?

Java 如何使用ANTLR v3构建日期/小时语法?,java,antlr,antlr3,Java,Antlr,Antlr3,我想用Antlr解析一串法国日期 我有三种类型的约会: 日期:2004年10月3日(小时/分钟为午夜) 日期\时间:12小时(以当前日期填写日、月、年) 完成日期:2004年10月3日12小时。您可以看到date\u complete:date\u day date\u hour 我要分析的文档只是一个由date\u day、date\u time和date\u complete组成的链(没有分隔符) 下面是我想要解析的字符串的示例 3 Octobre 2005 12h 13h 5 Octo

我想用Antlr解析一串法国日期

我有三种类型的约会:

  • 日期:2004年10月3日(小时/分钟为午夜)
  • 日期\时间:12小时(以当前日期填写日、月、年)
  • 完成日期:2004年10月3日12小时。您可以看到
    date\u complete:date\u day date\u hour
我要分析的文档只是一个由
date\u day
date\u time
date\u complete
组成的链(没有分隔符)

下面是我想要解析的字符串的示例

3 Octobre 2005 12h 13h 5 Octobre 2004 3 Septembre 2005 11h
Expected : date_complete date_time date_day date_complete

12h
Expected : date_time

3 Octobre 2005 5 Octobre 2004 12h 13h 3 Septembre 2005 11h
Expected : date_day date_complete date_time date_complete

**// NEW REQUIREMENTS**

3 Octobre 2005
Expected : date_day 

3 Octobre 
Expected : date_day 

3 
Expected : date_day 
我尝试了很多东西,Antlr v3总是说我的语法模棱两可:

warning(200): /meleo.dates/src/Grammar.g:25:48: 
Decision can match input such as "{FRI, MON..TUE, WED} TWO_DIGITS DECEMBER FOUR_DIGITS {FRI..HOURG, MON..WED}" using multiple alternatives: 1, 2
As a result, alternative(s) 2 were disabled for that input
 |---> date_day (date_day | date_complete | date_hour)+
写那种语法的正确方法是什么

语法如下:

grammar MeleoDates;

options {
  language = Java;
}

@header {
  package meleo.data.dates ; 

  import rainstudios.meleo.crawler.data.Dates ;
  import rainstudios.meleo.crawler.data.EventDate ;
}

@lexer::header {
  package meleo.data.dates ;   

  import rainstudios.meleo.crawler.data.EventDate ;
 }

input           returns [Dates dates] 
                @init {Dates r = new Dates() ; } : 
                (   date 
                    {r.addDay($date.date);}
                    DATE_SEP?)+ 
                EOF
                    {$dates = r ;}
                ;

date            returns [EventDate date] :
                (date_complete)=> date_complete 
                    {$date = $date_complete.date;}
                | date_day 
                    {$date = $date_day.date;}
                | date_time 
                    {$date = $date_time.date;}
                ;

date_complete  returns  [EventDate date]   
                @init   {EventDateBuilder builder = new EventDateBuilder() ; } : 
                 day=date_day 
                    {builder.addDay($day.date);}
                 HOUR_SEP? 
                 time=date_time 
                    {builder.addTime($time.date);}
                    {$date = builder.toDate();}
                ;

date_day        returns [EventDate date] 
                @init   {EventDateBuilder builder = new EventDateBuilder() ; } :
                (
                dayOfWeek=( 
                     MON
                   | TUE
                   | WED
                   | THU
                   | FRI
                   | SAT
                   | SUN
                )?
                (day=INT)=> INT 
                    {builder.addDay($day.text);}
                (   m=ID 
                        {builder.addMonth($m.text);}
                    year=INT ?
                        {builder.addMonth($year.text);}
                )?
                )
                    {$date = builder.toDate();}
                ;

date_time       returns [EventDate date]  
                @init   {EventDateBuilder builder = new EventDateBuilder() ; } :
                    TIME 
                    {builder.addTime($TIME.text);}
                    {$date = builder.toDate();}
                ;

month   : DECEMBER | JANUARY ;

MON 
 : 'lundi'  
 | 'lun' 
 ;

 TUE 
 : 'mardi'  
 | 'mar' 
 ;

 WED 
 : 'mercredi'  
 | 'mer' 
 ;

 THU 
 : 'jeudi'  
 | 'jeu' 
 ;

 FRI 
 : 'venredi'  
 | 'ven' 
 ;

 SAT 
 : 'samedi'  
 | 'sam' 
 ;

 SUN 
 : 'dimanche'  
 | 'dim' 
 ;

DECEMBER    : 'dec' | 'decembre' ;
JANUARY     : 'jan' | 'janvier' ;

DATE_SEP    : 'et'| ',' | '-'; 
HOUR_SEP    : 'à' | 'a' ;
INT         : ('0'..'9')+;
TIME_SEP    : ':'  | 'h' ;
TIME        : INT TIME_SEP INT?;
ID          : ('a'..'z'|'A'..'Z')+;

WS : (' ' | '\t' | '\n' | '\r' | '\f')+ {$channel = HIDDEN;};

**编辑:添加了新的要求(日期可选的月份和年份)**

我认为使用ANTLR不会带来任何好处。相反,您可以使用一些附加工作来检查拖尾小时(即“h”)标记,以实现以下目标:

一揽子问题

import java.text.ParseException;
import java.text.ParsePosition;
import java.text.SimpleDateFormat;
import java.util.Locale;

public class FrenchDateParser {
    private static SimpleDateFormat date_complete = new SimpleDateFormat("d MMMM yyyy h", Locale.FRENCH);

    private static SimpleDateFormat date_day = new SimpleDateFormat("d MMMM yyyy", Locale.FRENCH);

    private static SimpleDateFormat date_time = new SimpleDateFormat("h", Locale.FRENCH);

    private static String parse(String input) {
        ParsePosition parsePosition = new ParsePosition(0);
        StringBuilder stringBuilder = new StringBuilder();
        int inputSize = input.length();
        while (parsePosition.getIndex() < inputSize) {
            int startingParsePositionIndex = parsePosition.getIndex();

            if (date_complete.parse(input, parsePosition) != null) {
                if (input.charAt(parsePosition.getIndex()) == 'h') {
                    stringBuilder.append("date_complete ");
                    parsePosition.setIndex(parsePosition.getIndex() + 1);
                    continue;
                }
                parsePosition.setIndex(startingParsePositionIndex);
            }

            if (date_day.parse(input, parsePosition) != null) {
                stringBuilder.append("date_day ");
                continue;
            }

            if (date_time.parse(input, parsePosition) != null) {
                if (input.charAt(parsePosition.getIndex()) == 'h') {
                    stringBuilder.append("date_time ");
                    parsePosition.setIndex(parsePosition.getIndex() + 1);
                    continue;
                }
                parsePosition.setIndex(startingParsePositionIndex);
            }

            throw new IllegalArgumentException("Unable to parse input [" + input + "]");
        }
        return stringBuilder.toString().trim();
    }

    public static void main(String... args) throws ParseException {
        String[] inputs = {"3 Octobre 2005 12h 13h 5 Octobre 2004 3 Septembre 2005 11h", "12h",
                "3 Octobre 2005 5 Octobre 2004 12h 13h 3 Septembre 2005 11h"};

        String[] expecteds = {"date_complete date_time date_day date_complete", "date_time",
                "date_day date_complete date_time date_complete"};

        for (int i = 0; i < inputs.length; i++) {
            String actual = parse(inputs[i]);
            System.out.println(expecteds[i].equals(actual));
        }
    }
}
import java.text.ParseException;
导入java.text.ParsePosition;
导入java.text.simpleDataFormat;
导入java.util.Locale;
公共类FrenchDateParser{
私有静态SimpleDataFormat date_complete=新SimpleDataFormat(“d MMMM yyy h”,Locale.FRENCH);
私有静态SimpleDataFormat date\u day=新SimpleDataFormat(“d MMMM yyyy”,Locale.FRENCH);
私有静态SimpleDataFormat日期\时间=新SimpleDataFormat(“h”,Locale.FRENCH);
私有静态字符串解析(字符串输入){
ParsePosition ParsePosition=新的ParsePosition(0);
StringBuilder StringBuilder=新的StringBuilder();
int inputSize=input.length();
while(parsePosition.getIndex()
考虑使用语法谓词:

input      : date+;
date       : (date_complete) => date_complete
           | date_day
           | date_time
           ;
实际上,这告诉ANTLR在尝试匹配它通常找到的任何内容之前,先尝试一个
日期_complete
(这可能不是一个技术上准确的描述,但你知道了)。如果没有这一点,
date
规则可以使用相同的输入匹配多个选项,而ANTLR(无论如何,是v3)无法解决这一问题

以下是完整的测试语法:

grammar AmbiguousDates;


input           : date+ EOF;

date            : (date_complete)=> date_complete 
                    {System.out.println("date_complete: " + $date_complete.str);}
                | date_day 
                    {System.out.println("date_day: " + $date_day.str);}
                | date_time 
                    {System.out.println("date_time: " + $date_time.str);}
                ;

date_complete   returns [String str]
                : date_day date_time 
                    {$str = String.format("\%s \%s", $date_day.str, $date_time.str);}
                ;

date_day        returns [String str]
                : day=INT ID year=INT 
                    {$str = String.format("\%s \%s \%s", $day.text, $ID.text, $year.text);}
                ;

date_time       returns [String str]
                : TIME 
                    {$str = $TIME.text;}
                ;

INT     : ('0'..'9')+;
TIME    : INT 'h';
ID      : ('a'..'z'|'A'..'Z')+;
WS      : (' '|'\t'|'\f'|'\r'|'\n')+ {skip();};
输入 输出
你可以在这里发布你的语法吗?现在,根据@tenterhook suggestionI的语法,为了这个问题,我简化了日期和时间。使用ANTLR仍然不能确保有效的日期,例如闰年,并且会生成你想要的默认值(例如,“日期”小时/分钟是午夜)因此,我们仍然没有看到ANTLR通过Java的内置解析器提供了什么。使用Java内置的日期解析器可以同时为您进行解析和验证。下一步是添加间隔:“从12h30到15h45”和许多其他内容,比如解析少量自然语言“午夜,中午,明天”。一个合适的解析器看起来是可行的。现在,我想让月份和年份成为可选的<代码>日期返回[String str]:day=INT ID?年份=整数date\u day不知道
INT
的意思是“date\u day=day year”还是“date\u day=day date\u day=day”。尝试制定词法规则,如匹配一个或两个数字的
DAY
,匹配四个数字的
YEAR
,然后将
date\u DAY
更改为
DAY ID?年份?
如果遇到问题,开始新问题可能会更容易。
3 Octobre 2005 12h 13h 5 Octobre 2004 3 Septembre 2005 11h
date_complete: 3 Octobre 2005 12h
date_time: 13h
date_day: 5 Octobre 2004
date_complete: 3 Septembre 2005 11h