我目前正在使用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
;
这是我感兴趣的部分:
感谢。
示例(输入)
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!
}
答案 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()。