描述cron表达式的Xtext语法不能按预期工作

时间:2015-06-17 00:43:12

标签: antlr grammar dsl context-free-grammar xtext

我目前正在使用Xtext开发领域特定语言,直到现在一切都进展顺利。在这个时刻,我正在研究语法的定义,特别是在一组生成规则中让用户指定cron表达式(就像Unix中crontab文件中的那些)。

问题:正如您所看到的,生产规则RangeCronList应该允许*/1,3-4,JAN-DEC这样的值,但事实并非如此。但是,它确实允许*/10,10-2*/1,JAN-DEC之类的内容。

错误:我在生成的Eclipse IDE中获得的错误(对于未识别的表达式)是“输入时没有可行的替代方法......”。

问题:为什么这些生产规则不允许指定整数或ID?我希望用户能够指定单个整数,id,它们的范围以及这些可能值的列表。

附加:我对DSL的体验很短,所以如果你能对这个语法片段提出一些建议我很感激。

grammar org.pascani.Pascani with org.eclipse.xtext.xbase.Xbase

import "http://www.eclipse.org/xtext/common/JavaVMTypes" as types

generate pascani "http://www.pascani.org/Pascani"

Model
    :   package = PackageDeclaration? 
        imports = XImportSection?
        usings =  UsingSection?
        typeDeclaration = TypeDeclaration?
    ;

PackageDeclaration returns Package
    :   'package' name = QualifiedName ';'?
    ;

UsingSection
    :   usingDeclarations += UsingDeclaration+
    ;

UsingDeclaration returns Using
    :   'using' namespace ?= 'namespace' type = [Namespace | QualifiedName] ';'?
    ;

TypeDeclaration
    :   MonitorDeclaration 
    |   NamespaceDeclaration
    ;

MonitorDeclaration returns Monitor
    :   'monitor' name = ID '{'
            typeDeclarations += MemberDeclaration*
        '}'
    ;

NamespaceDeclaration returns Namespace
    :   'namespace' name = ID '{'
            typeDeclarations += NamespaceMemberDeclaration*
        '}'
    ;

NamespaceMemberDeclaration 
    :   NamespaceDeclaration 
    |   VariableDeclaration
    ;

MemberDeclaration
    :   VariableDeclaration
    |   HandlerDeclaration
    |   EventDeclaration
    ;

VariableDeclaration
    :   jvmType = JvmTypeReference expression = XExpression ';'?    // (XAssignment | MapValue | PairValue | ArrayValue) 
    ;

HandlerDeclaration returns Handler
    :   'handler' name = ID '(' declaredFormalParameter = JvmFormalParameter ')' body = XBlockExpression
    ;

// Special data types declaration

MapValue returns Map    
    :   {Dictionary} '{' ( pairs += [Pair] (',' pairs += [Pair])* )? '}'
    ;

PairValue returns Pair
    :   key = (ID | STRING) ':' value = XExpression
    ;

ArrayValue returns Array
    :   {Array} '[' (elements += XExpression (',' elements+= XExpression)*)? ']'
    ;

// Event declarations

EventDeclaration returns Event
    :   'event' name = ID 'raised' (periodically ?= 'periodically')? 'on' emitter = EventEmitter ';'?
    ;

EventType
    :   ('invoke'|'return'|'change'|'exception')
    ;

EventEmitter
    :   eventType = EventType 'of' emitter = QualifiedName (=>specifier = (RelationalEventSpecifier | EventSpecifier))? ('using' probe = ID)?
    |   cronExpression = CronExpression
    ;

RelationalEventSpecifier
    :   '(' RelationalEventSpecifier ')'
    |   left = EventSpecifier (and ?= 'and' | or ?= 'or') right = EventSpecifier
    ;

EventSpecifier
    :   'below' 'of' EventSpecifierValue
    |   'above' 'of' EventSpecifierValue
    |   'equal' 'to' EventSpecifierValue
    ;

EventSpecifierValue
    :   value = Number percentage ?= '%'?
    |   variable = QualifiedName
    ;

CronExpression
    :   '('
            seconds = CronElement
            minutes = CronElement 
            hours = CronElement  
            days = CronElement 
            months = CronElement  
            daysOfWeek = CronElement 
            (year = CronElement)?
        ')'
    |   '@' constant = ID
    ;

CronElement
    :   TerminalCronElement | RangeCronElement | PeriodicCronElement
    ;

RangeCronElement hidden()
    :   start = IntLiteral '-' end = IntLiteral
    |   start = ID '-' end = ID
    ;

TerminalCronElement
    :   expression = (IntLiteral | ID | '*' | '?')
    ;

PeriodicCronElement hidden()
    :   expression = TerminalCronElement '/' elements = RangeCronList
    ;

RangeCronList hidden()
    :   elements += (TerminalCronElement | RangeCronElement) (',' elements += (TerminalCronElement | RangeCronElement))*
    ;

IntLiteral
    :   INT
    ;

这是我感兴趣的部分:

Cron Expression

感谢。

示例(输入)

package org.example.monitors

using namespace System

monitor Performance {

    event e1 raised on (0 */1,10-20 * * * *) // works fine
    event e2 raised on (0 */1 * * * *) // It's not recognized!

}

1 个答案:

答案 0 :(得分:1)

我没有看到空格对于解析有多重要。如果你只是想禁止空格,那么最好在之后使其失效,因为这样你就可以更好地控制你告诉用户的内容和方式(即'意外的令牌RULE_WS'并不是非常有用)。

这里的问题是,我们需要先查看2才能决定是否输入规则TerminalCronElement或RangeCronElement。两种选择都检查LA(2)处的令牌以寻找可能的后续行动。不幸的是,因为我们处于hidden()上下文中,所以令牌是WS,但是未将其列为可能的后续行动,因为后续令牌来自的规则会隐藏空格。

你的语法是有效的,如果你有点像这样重写有问题的部分:

Model:
   expressions+=CronExpression*;

CronExpression
   :    '('
           seconds = CronElement
           minutes = CronElement 
           hours = CronElement  
           days = CronElement 
           months = CronElement  
           daysOfWeek = CronElement 
           (year = CronElement)?
       ')'
   |     '@' constant = ID
   ;

CronElement
   :    RangeCronElement | PeriodicCronElement
   ;

RangeCronElement hidden()
   :    TerminalCronElement ({RangeCronElement.start=current}'-' end = IntLiteral)*
   ;

TerminalCronElement
   :    expression = (IntLiteral | ID | '*' | '?')
   ;

PeriodicCronElement hidden()
   :    expression = TerminalCronElement '/' elements = RangeCronList
   ;

RangeCronList hidden()
   :    elements += RangeCronElement (',' elements +=RangeCronElement)*
   ;

IntLiteral
   :    INT
   ;

(注意RangeCronElement的变化) 但在这里你必须使诸如* -3或?-5之类的东西无效。

我建议你尝试不使用hidden()。

相关问题