問題は、レクサーやパーサーで構文チェックと意味チェックの両方を実行したいように見えることです。これはよくある間違いであり、非常に単純な言語でのみ発生する可能性があります。
本当に必要なのは、レクサーとパーサーでより広く受け入れてから、セマンティック チェックを実行することです。レクシングをどの程度厳密にするかはあなた次第ですが、月の日の前にゼロを受け入れる必要があるかどうかに応じて、2 つの基本的なオプションがあります。1) INT を実際に受け入れる、2) DATENUM を定義して有効な日であり、有効な INT ではないトークンのみを受け入れます。コードの後半で必要なセマンティック チェックが少なくなるため、2 番目の方法をお勧めします (INT は構文レベルで検証可能になり、日付に対してセマンティック チェックのみを実行する必要があるためです。最初のアプローチ:
INT: '0'..'9'+;
2 番目のアプローチ:
DATENUM: '0' '1'..'9';
INT: '0' | SIGN? '1'..'9' '0'..'9'*;
レクサーでこれらのルールを使用して受け入れた後、日付フィールドは次のいずれかになります。
date: INT '/' INT ( '/' INT )?
また:
date: (INT | DATENUM) '/' (INT | DATENUM) ('/' (INT | DATENUM) )?
その後、日付が有効であることを確認するために、AST に対してセマンティック ランを実行します。
ただし、文法でセマンティック チェックを実行することに固執している場合、ANTLR はパーサーでセマンティック述語を許可するため、次のように値をチェックする日付フィールドを作成できます。
date: month=INT '/' day=INT ( year='/' INT )? { year==null ? (/* First check /*) : (/* Second check */)}
ただし、これを行うと、文法に言語固有のコードが埋め込まれ、ターゲット間で移植できなくなります。