2017-03-14 72 views
1

我試圖解析使用以下語法日期:錯誤在解析日期Antlr4

grammar Dates; 

formattedDate : (DATE '/' MONTH '/' year); 
year : SHORT_YEAR | FULL_YEAR; 

SHORT_YEAR : DIGIT DIGIT; 
FULL_YEAR : ('19' | '20' | '21') DIGIT DIGIT; 
DATE : (('0'..'2')? DIGIT) | '30' | '31'; 
MONTH : ('0'? DIGIT) | '11' | '12'; 

fragment DIGIT : ('0' .. '9'); 

但它無法解析,我預期的那樣工作的值。例如,11/04/2017輸入產生錯誤:

line 1:0 mismatched input '11' expecting DATE 

我的第一個猜測是,有一些值(1-12)的詞法分析器不能決定,如果它是一個DATEMONTH,這是造成問題。但是,當我試圖用語法規則代替他們,而不是修復它,我有同樣的問題:

formattedDate : (dateNum '/' monthNum '/' year); 

year : shortYear | fullYear; 
shortYear : DIGIT DIGIT; 
fullYear : ('19' | '20' | '21') DIGIT DIGIT; 
dateNum : (('0'..'2')? DIGIT) | '30' | '31'; 
monthNum : ('0'? DIGIT) | '11' | '12'; 

fragment DIGIT : ('0' .. '9'); 

而且它似乎仍然對第一值鬥爭,即使它像31,範圍之外歧義。

我在這裏做錯了什麼?

+0

這也許顯而易見的,爲什麼我第一次嘗試不起作用。令牌重疊,因此詞法分析器不可能是明確的。我在這裏也看到了很多關於其他問題的建議,說不要試圖在語法中加入太多的限制,而是在解析之後留下那些驗證錯誤。但是,我仍然想明白爲什麼這個特殊情況不起作用。 –

回答

1

正如你所說,「代幣重疊」(注意31含糊不清,可能是短暫的一年)。在這種情況下,將選擇最長的匹配詞法分析規則。如果有兩個或更多的匹配長度相同,它會選擇第一個(按它們出現的順序)。 (我想我已經在www.antlr.org閱讀前一段時間)

所以才改變規則的順序「解決」的問題 - 或向前推動它(注意DATESHORT_YEARMONTH前):

grammar Dates; 

formattedDate : (DATE '/' MONTH '/' year); 
year : SHORT_YEAR | FULL_YEAR; 

DATE : (('0'..'2')? DIGIT) | '30' | '31'; 
SHORT_YEAR : DIGIT DIGIT; 
FULL_YEAR : ('19' | '20' | '21') DIGIT DIGIT; 
MONTH : ('0'? DIGIT) | '11' | '12'; 

fragment DIGIT : ('0' .. '9'); 

收率line 1:3 mismatched input '04' expecting MONTH


一種可能的解決方案是使用詞法分析器語法模式:

DatesLexer.g4:

lexer grammar DatesLexer; 

// Mode expecting DATE (default mode) 
DATE : (('0'..'2')? DIGIT) | '30' | '31'; 
DATE_BAR : '/' 
    -> pushMode(readingMonth); 

// Mode expecting MONTH 
mode readingMonth; 
MONTH : ('0'? DIGIT) | '11' | '12'; 
MONTH_BAR : '/' 
    -> popMode, pushMode(readingYear); 

// Mode expecting *_YEAR 
mode readingYear; 
SHORT_YEAR : DIGIT DIGIT 
    -> popMode; 
FULL_YEAR : ('19' | '20' | '21') DIGIT DIGIT 
    -> popMode; 

fragment DIGIT : ('0' .. '9'); 

DatesParser.g4:

parser grammar DatesParser; 

options { tokenVocab=DatesLexer; } 

formattedDate : (DATE DATE_BAR MONTH MONTH_BAR year); 
year : SHORT_YEAR | FULL_YEAR; 

結果:

A parse tree showing that the date 11/04/2017 was successfully parsed

僅供參考:

> antlr4 DatesLexer.g4 [-o outDir] 
> antlr4 DatesParser.g4 [-o outDir] 
> [cd outDir] 
> javac *.java 
> grun Dates formattedDate -tokens <file> [-gui] 
[@0,0:1='11',<1>,1:0] 
[@1,2:2='/',<2>,1:2] 
[@2,3:4='04',<3>,1:3] 
[@3,5:5='/',<4>,1:5] 
[@4,6:9='2017',<6>,1:6] 
[@5,10:9='<EOF>',<-1>,1:10]