Home » Modeling » TMF (Xtext) » designing grammar for correct scoping context
designing grammar for correct scoping context [message #775163] |
Thu, 05 January 2012 13:31 |
Christophe Bouhier Messages: 937 Registered: July 2009 |
Senior Member |
|
|
Hello,
Are there some grammar rules documented, which should be followed to know what kind of context will be obtained by scoping? In the grammar I am working on, I use simple actions an return types like this:
ContextRef returns Reference:
{ContextRef} 'this' (primaryRef=PrimaryRef | rangeRef=RangeRef);
PrimaryRef returns Reference:
(components+=OperatorRef)+ ('->' leafRef=LeafReference)?;
ResourceRef returns LeafReference:
{ResourceRef} 'RESOURCE' (resource=[library::BaseResource | FQN] | all?='ALL') rangeRef=RangeRef;
with declarative scoping, I would like to reduce the visible set of x-references in the rule "ResourceRef" example, however I find my scope is not "ResourceRef" (as I would expect), but in my case the root type of the grammar . I guess ResourceRef is not created yet, but what are the rules of thumb to know which context to expect in the scope or how do I define the grammar so that ResourceRef is the context ( I would even be happy if the ContextRef rule was the context)?
It sounds like I am asking, how the lazy creation works in relation to Actions in the grammar?.
thank you, Christophe
Full grammar below here....
/*******************************************************************************
NetXScript interpreter.
Supports:
Arithmetics
Functions
Variables
Keyword substitution, and referenced model/feature lookup.
*******************************************************************************/
grammar com.netxforge.Netxscript with org.eclipse.xtext.common.Terminals
generate netxscript "http://www.netxforge.com/Netxscript"
// Declare an alias to access ecore types.
import "http://www.eclipse.org/emf/2002/Ecore" as ecore
// Note we can't use the nsURI, as it won't be found in development time. On the other hand the resource
// uri won't be found in runtime???
//import "platform:/resource/com.netxforge.model.a/model/library.ecore" as library
import "platform:/resource/com.netxforge.netxstudio.models/models/cdo/13042011/library.ecore" as library
import "platform:/resource/com.netxforge.netxstudio.models/models/cdo/13042011/operators.ecore" as operator
import "platform:/resource/com.netxforge.netxstudio.models/models/cdo/13042011/metrics.ecore" as metric
Mod:
('mod' name=ID)?
(imports+=Import)*
((functions+=Function)+ |
(statements+=Statement)+);
Import:
'import' importURI=STRING;
Function:
'def' name=ID '(' (args+=Argument (',' args+=Argument)*)? ')' block=Block;
Argument:
name=ID ;
AbstractVarOrArgument:
VariableStatement | Argument;
AbstractFunction:
Function;
Block:
{Block} '{' statements+=Statement* '}';
Statement:
(VariableStatement | AssignmentStatement | PlusAssignmentStatement | ReferenceAssignmentStatement | expression=Expression | ReturnStatement) ';'
| IfStatement | WhileStatement | Block;
ReturnStatement returns Statement:
{Return} 'return' (expression=Expression)?;
IfStatement returns Statement:
{If} 'if' '(' if=Logical ')' then=Block ('else' else=Block)?;
WhileStatement returns Statement:
{While} 'while' '(' predicate=Logical ')' body=Block;
VariableStatement returns Statement:
{Variable} 'var' name=ID (('=') expression=Expression)?;
AssignmentStatement returns Statement:
{Assignment} var=[AbstractVarOrArgument] '=' expression=Expression;
PlusAssignmentStatement returns Statement:
{PlusAssignment} var=[AbstractVarOrArgument] '+=' expression=Expression;
//AssignmentStatement returns Statement:
// VarOrArgumentCall ({IndexedAssignment.assignment=current})? '=' expression=Expression;
ReferenceAssignmentStatement returns Statement:
{RefAssignment} assignmentRef=(ContextRef | NodeTypeRef ) '=' expression=Expression;
// Expression, all operators extend this class.
Expression:
Logical;
// Logical
Logical returns Expression:
Equality (({And.left=current} '&&' | {Or.left=current} '||') right=Equality)*;
// Equal, Non-Equal
Equality returns Expression:
Comparison (({Equal.left=current} '==' | {Unequal.left=current} '!=') right=Comparison)*;
// Lesser, Greater.
Comparison returns Expression:
Addition (({Lesser.left=current} '<' | {LesserEqual.left=current} '<=' | {Greater.left=current} '>' |
{GreaterEqual.left=current} '>=') right=Addition)*;
Addition returns Expression:
Multiplication (({Plus.left=current} '+' | {Minus.left=current} '-') right=Multiplication)*;
Multiplication returns Expression:
Unary (({Multi.left=current} '*' | {Div.left=current} '/' | {Modulo.left=current} '%') right=Unary)*;
// Unaries are return type specific. The type on the right should be checked.
Unary returns Expression:
PrimaryExpression | ({Negation} op='!' | {UnaryPlusMinus} op=UnaryOperator) right=Unary;
UnaryOperator:
'-' | '+';
// The FunctionCall and potential parameters, shoul be checked as reference priot to a regular Assignment call.
PrimaryExpression returns Expression:
Literal | Range | NativeExpression | Reference | FunctionCall | IndexedCall | ParenthesizedExpression;
Literal returns Expression:
NumberLiteral | {BooleanLiteral} (condition?='true' | 'false');
NumberLiteral returns Expression:
{NumberLiteral} value=NUMBER
;
ParenthesizedExpression returns Expression:
'(' Expression ')';
FunctionCall returns Expression:
{FunctionCall} func=[AbstractFunction] '(' (args+=Expression (',' args+=Expression)*)? ')';
IndexedCall returns Expression:
VarOrArgumentCall ('[' index=Expression ']')?
;
NativeExpression returns Expression:
{NativeExpression} (range=RangeLiteral | ref=Reference | var=VarOrArgumentCall) '.' nativeFunction=NativeFunction;
VarOrArgumentCall returns Expression:
{VarOrArgumentCall} call=[AbstractVarOrArgument];
Range:
RangeLiteral;
RangeLiteral returns Range:
{RangeLiteral} '[' (values+=NUMBER (',' values+=NUMBER)*)? ']';
enum NativeFunction:
COUNT='count()' | SUM='sum()' | MIN='min()' | MAX='max()' | MEAN='mean()' | DEVIATION='deviation()' | ERLANGB='erlangB()' | CLEAR ='clear()';
Reference returns Expression:
ContextRef | NodeTypeRef | NodeRef | ParamRef
;
ParamRef returns Reference:
{ParamRef} 'PARAM' param=[library::Parameter | FQN]
;
/**
* The Node context can be found with NODE if available.
*/
NodeTypeRef returns Reference:
{NodeTypeRef} 'NETYPE' nodetype=[library::NodeType] primaryRef=PrimaryRef
;
NodeRef returns Reference:
{NodeRef} 'NE' node=[operator::Node] primaryRef=PrimaryRef
;
/**
* Contextual reference will be restricted by a container in the scope provider
* depending on the object representation of 'this', until implemented, all possible
* references will be visible.
*/
ContextRef returns Reference:
{ContextRef} 'this' (primaryRef=PrimaryRef | rangeRef=RangeRef);
/**
* Exteral Node references, these can be nested and finish with a resource reference.
*/
PrimaryRef returns Reference:
(components+=OperatorRef)+ ('->' leafRef=LeafReference)?;
OperatorRef returns Reference:
{OperatorRef} '.' ( 'FUNCTION' function=[library::Function | FQN] | 'EQUIPMENT' equipment=[library::Equipment | FQN] | 'PROFILE' | 'STATUS')
;
LeafReference returns LeafReference:
ResourceRef | LinkRef | StatusRef;
ResourceRef returns LeafReference:
{ResourceRef} 'RESOURCE' (resource=[library::BaseResource | FQN] | all?='ALL') rangeRef=RangeRef;
StatusRef returns LeafReference:
{StatusRef} tolerancelevel=ToleranceLevel;
/**
* Optional value kind AVG / BH
* Optional Number value (In minutes like 60, etc..)
*
*/
RangeRef:
valuerange=ValueRange (kind=ValueKind)? (interval = Interval)?
;
Interval:
interval=NUMBER | kind=IntervalKind
;
enum IntervalKind:
MONTH="MONTH" | WEEK="WEEK" | DAY="DAY" | HOUR="HOUR"
;
enum ToleranceLevel:
RED='RED' | AMBER='AMBER' | GREEN='GREEN' | YELLOW='YELLOW'
;
enum ValueRange:
METRIC='METRIC' | CAP='CAP' | FORECAST='FORECAST' | FORECAST_CAP='FORECAST_CAP' | TRENDED='TRENDED' | UTILIZATION='UTILIZATION' | TOLERANCE='TOLERANCE' | DERIVED="DERIVED"
;
enum ValueKind:
AVG='AVG' | BH='BH'
;
/**
* TODO, through a link, we should be able to access, the opposite node and
* resources.
*
*/
LinkRef returns LeafReference:
{LinkRef} 'LINK' link=[operator::Relationship]
;
FQN :
SPACED_ID ('::' SPACED_ID)*
;
SPACED_ID returns ecore::EString:
(ID | NUMBER) (ID | NUMBER )*
;
terminal NUMBER returns ecore::EBigDecimal:
('0'..'9')* ('.' ('0'..'9')+)?;
terminal INT returns ecore::EInt:
'zzzzzzzzz';
/*
* - Can have quotes and dash.
*/
terminal ID : '^'?('a'..'z'|'A'..'Z'|'_'|'-'|'('| ')') ('a'..'z'|'A'..'Z'|'_'|'0'..'9'|'-'|'('| ')')*;
|
|
|
Re: designing grammar for correct scoping context [message #775174 is a reply to message #775163] |
Thu, 05 January 2012 13:57 |
Sebastian Zarnekow Messages: 3118 Registered: July 2009 |
Senior Member |
|
|
Hi Christophe,
please provide some more information about your scope provider.
How does your method signature look like and how is it called?
Regards,
Sebastian
--
Need professional support for Eclipse Modeling?
Go visit: http://xtext.itemis.com
Am 05.01.12 14:31, schrieb Christophe Bouhier:
> Hello,
> Are there some grammar rules documented, which should be followed to
> know what kind of context will be obtained by scoping? In the grammar I
> am working on, I use simple actions an return types like this:
>
> ContextRef returns Reference:
> {ContextRef} 'this' (primaryRef=PrimaryRef | rangeRef=RangeRef);
>
> PrimaryRef returns Reference:
> (components+=OperatorRef)+ ('->' leafRef=LeafReference)?;
>
> ResourceRef returns LeafReference:
> {ResourceRef} 'RESOURCE' (resource=[library::BaseResource | FQN] |
> all?='ALL') rangeRef=RangeRef;
>
>
> with declarative scoping, I would like to reduce the visible set of
> x-references in the rule "ResourceRef" example, however I find my scope
> is not "ResourceRef" (as I would expect), but in my case the root type
> of the grammar . I guess ResourceRef is not created yet, but what are
> the rules of thumb to know which context to expect in the scope or how
> do I define the grammar so that ResourceRef is the context ( I would
> even be happy if the ContextRef rule was the context)?
> It sounds like I am asking, how the lazy creation works in relation to
> Actions in the grammar?.
> thank you, Christophe
>
> Full grammar below here....
>
>
> /*******************************************************************************
>
> NetXScript interpreter. Supports:
> Arithmetics
> Functions
> Variables
> Keyword substitution, and referenced model/feature lookup.
>
> *******************************************************************************/
>
> grammar com.netxforge.Netxscript with org.eclipse.xtext.common.Terminals
>
> generate netxscript "http://www.netxforge.com/Netxscript"
>
> // Declare an alias to access ecore types. import
> "http://www.eclipse.org/emf/2002/Ecore" as ecore
> // Note we can't use the nsURI, as it won't be found in development
> time. On the other hand the resource
> // uri won't be found in runtime??? //import
> "platform:/resource/com.netxforge.model.a/model/library.ecore" as library
> import
> "platform:/resource/com.netxforge.netxstudio.models/models/cdo/13042011/library.ecore"
> as library
> import
> "platform:/resource/com.netxforge.netxstudio.models/models/cdo/13042011/operators.ecore"
> as operator
> import
> "platform:/resource/com.netxforge.netxstudio.models/models/cdo/13042011/metrics.ecore"
> as metric
>
> Mod:
> ('mod' name=ID)?
> (imports+=Import)*
> ((functions+=Function)+ |
> (statements+=Statement)+);
>
> Import:
> 'import' importURI=STRING;
>
> Function:
> 'def' name=ID '(' (args+=Argument (',' args+=Argument)*)? ')' block=Block;
>
> Argument:
> name=ID ;
>
> AbstractVarOrArgument:
> VariableStatement | Argument;
>
> AbstractFunction:
> Function;
>
> Block:
> {Block} '{' statements+=Statement* '}';
>
> Statement:
> (VariableStatement | AssignmentStatement | PlusAssignmentStatement |
> ReferenceAssignmentStatement | expression=Expression | ReturnStatement) ';'
> | IfStatement | WhileStatement | Block;
>
> ReturnStatement returns Statement:
> {Return} 'return' (expression=Expression)?;
>
> IfStatement returns Statement:
> {If} 'if' '(' if=Logical ')' then=Block ('else' else=Block)?;
>
> WhileStatement returns Statement:
> {While} 'while' '(' predicate=Logical ')' body=Block;
>
> VariableStatement returns Statement:
> {Variable} 'var' name=ID (('=') expression=Expression)?;
>
> AssignmentStatement returns Statement:
> {Assignment} var=[AbstractVarOrArgument] '=' expression=Expression;
>
> PlusAssignmentStatement returns Statement:
> {PlusAssignment} var=[AbstractVarOrArgument] '+=' expression=Expression;
>
> //AssignmentStatement returns Statement:
> // VarOrArgumentCall ({IndexedAssignment.assignment=current})? '='
> expression=Expression;
>
> ReferenceAssignmentStatement returns Statement:
> {RefAssignment} assignmentRef=(ContextRef | NodeTypeRef ) '='
> expression=Expression;
>
> // Expression, all operators extend this class. Expression:
> Logical;
>
> // Logical
> Logical returns Expression:
> Equality (({And.left=current} '&&' | {Or.left=current} '||')
> right=Equality)*;
>
> // Equal, Non-Equal Equality returns Expression:
> Comparison (({Equal.left=current} '==' | {Unequal.left=current} '!=')
> right=Comparison)*;
>
> // Lesser, Greater. Comparison returns Expression:
> Addition (({Lesser.left=current} '<' | {LesserEqual.left=current} '<=' |
> {Greater.left=current} '>' |
> {GreaterEqual.left=current} '>=') right=Addition)*;
>
> Addition returns Expression:
> Multiplication (({Plus.left=current} '+' | {Minus.left=current} '-')
> right=Multiplication)*;
>
> Multiplication returns Expression:
> Unary (({Multi.left=current} '*' | {Div.left=current} '/' |
> {Modulo.left=current} '%') right=Unary)*;
>
> // Unaries are return type specific. The type on the right should be
> checked. Unary returns Expression:
> PrimaryExpression | ({Negation} op='!' | {UnaryPlusMinus}
> op=UnaryOperator) right=Unary;
>
> UnaryOperator:
> '-' | '+';
>
> // The FunctionCall and potential parameters, shoul be checked as
> reference priot to a regular Assignment call. PrimaryExpression returns
> Expression:
> Literal | Range | NativeExpression | Reference | FunctionCall |
> IndexedCall | ParenthesizedExpression;
>
>
> Literal returns Expression:
> NumberLiteral | {BooleanLiteral} (condition?='true' | 'false');
>
> NumberLiteral returns Expression:
> {NumberLiteral} value=NUMBER
> ;
>
> ParenthesizedExpression returns Expression:
> '(' Expression ')';
>
> FunctionCall returns Expression:
> {FunctionCall} func=[AbstractFunction] '(' (args+=Expression (','
> args+=Expression)*)? ')';
>
> IndexedCall returns Expression:
> VarOrArgumentCall ('[' index=Expression ']')?
> ;
>
> NativeExpression returns Expression:
> {NativeExpression} (range=RangeLiteral | ref=Reference |
> var=VarOrArgumentCall) '.' nativeFunction=NativeFunction;
>
>
> VarOrArgumentCall returns Expression:
> {VarOrArgumentCall} call=[AbstractVarOrArgument];
>
> Range:
> RangeLiteral;
>
> RangeLiteral returns Range:
> {RangeLiteral} '[' (values+=NUMBER (',' values+=NUMBER)*)? ']';
>
> enum NativeFunction:
> COUNT='count()' | SUM='sum()' | MIN='min()' | MAX='max()' |
> MEAN='mean()' | DEVIATION='deviation()' | ERLANGB='erlangB()' | CLEAR
> ='clear()';
>
> Reference returns Expression:
> ContextRef | NodeTypeRef | NodeRef | ParamRef
> ;
>
> ParamRef returns Reference:
> {ParamRef} 'PARAM' param=[library::Parameter | FQN]
> ;
>
> /**
> * The Node context can be found with NODE if available.
> */
> NodeTypeRef returns Reference:
> {NodeTypeRef} 'NETYPE' nodetype=[library::NodeType] primaryRef=PrimaryRef
> ;
>
> NodeRef returns Reference:
> {NodeRef} 'NE' node=[operator::Node] primaryRef=PrimaryRef
> ;
>
> /**
> * Contextual reference will be restricted by a container in the scope
> provider
> * depending on the object representation of 'this', until implemented,
> all possible * references will be visible. */
>
> ContextRef returns Reference:
> {ContextRef} 'this' (primaryRef=PrimaryRef | rangeRef=RangeRef);
>
> /**
> * Exteral Node references, these can be nested and finish with a
> resource reference.
> */
>
> PrimaryRef returns Reference:
> (components+=OperatorRef)+ ('->' leafRef=LeafReference)?;
>
> OperatorRef returns Reference:
> {OperatorRef} '.' ( 'FUNCTION' function=[library::Function | FQN] |
> 'EQUIPMENT' equipment=[library::Equipment | FQN] | 'PROFILE' | 'STATUS')
> ;
>
> LeafReference returns LeafReference:
> ResourceRef | LinkRef | StatusRef;
>
> ResourceRef returns LeafReference:
> {ResourceRef} 'RESOURCE' (resource=[library::BaseResource | FQN] |
> all?='ALL') rangeRef=RangeRef;
>
> StatusRef returns LeafReference:
> {StatusRef} tolerancelevel=ToleranceLevel;
>
> /**
> * Optional value kind AVG / BH
> * Optional Number value (In minutes like 60, etc..)
> * */
> RangeRef:
> valuerange=ValueRange (kind=ValueKind)? (interval = Interval)?
> ;
>
> Interval: interval=NUMBER | kind=IntervalKind
> ;
> enum IntervalKind:
> MONTH="MONTH" | WEEK="WEEK" | DAY="DAY" | HOUR="HOUR" ;
>
> enum ToleranceLevel:
> RED='RED' | AMBER='AMBER' | GREEN='GREEN' | YELLOW='YELLOW'
> ;
>
>
> enum ValueRange: METRIC='METRIC' | CAP='CAP' | FORECAST='FORECAST' |
> FORECAST_CAP='FORECAST_CAP' | TRENDED='TRENDED' |
> UTILIZATION='UTILIZATION' | TOLERANCE='TOLERANCE' | DERIVED="DERIVED"
> ;
>
> enum ValueKind: AVG='AVG' | BH='BH'
> ;
>
>
> /**
> * TODO, through a link, we should be able to access, the opposite node
> and * resources. * */
> LinkRef returns LeafReference:
> {LinkRef} 'LINK' link=[operator::Relationship]
> ;
>
> FQN :
> SPACED_ID ('::' SPACED_ID)*
> ;
>
> SPACED_ID returns ecore::EString:
> (ID | NUMBER) (ID | NUMBER )*
> ;
>
> terminal NUMBER returns ecore::EBigDecimal:
> ('0'..'9')* ('.' ('0'..'9')+)?;
>
> terminal INT returns ecore::EInt:
> 'zzzzzzzzz';
>
> /*
> * - Can have quotes and dash. */
> terminal ID : '^'?('a'..'z'|'A'..'Z'|'_'|'-'|'('| ')')
> ('a'..'z'|'A'..'Z'|'_'|'0'..'9'|'-'|'('| ')')*;
>
>
>
|
|
| | | | | | |
Re: designing grammar for correct scoping context [message #775242 is a reply to message #775229] |
Thu, 05 January 2012 16:11 |
Sebastian Zarnekow Messages: 3118 Registered: July 2009 |
Senior Member |
|
|
Hi Christophe,
content assist has to deal with incomplete models. The parser may decide
to choose another path than you'd expect, e.g. even though the keyword
RESOURCE is present, it may use another alternative especially if you
have backtracking enabled in your grammar. If no instance of ResourceRef
is available, non can be passed to the scope provider. Therefore it'll
use a container that may use a possible to-be-created ResourceRef as its
transitive content. Thus the behavior that you observed. Do you have
backtracking enabled?
Regards,
Sebastian
--
Need professional support for Eclipse Modeling?
Go visit: http://xtext.itemis.com
Am 05.01.12 16:46, schrieb Christophe Bouhier:
> Hi,
> For a valid statement with the following input:
> this. FUNCTION SW Licenses -> RESOURCE STP_ORIGMSUS CAP;
>
> ...it's called several times.
> SCOPING: custom scoping called for scope_ResourceRef_resource with
> context type=ResourceRef
> SCOPING: custom scoping called for scope_ResourceRef_resource with
> context type=Reference
> SCOPING: custom scoping called for scope_ResourceRef_resource with
> context type=ContextRef
> SCOPING: custom scoping called for scope_ResourceRef_resource with
> context type=Statement
> SCOPING: custom scoping called for scope_ResourceRef_resource with
> context type=Mod
>
>
> but when the content assist is invoked here: it's only called once. ('|'
> is the cursor position).
> this. FUNCTION SW Licenses -> RESOURCE |
>
>
> It kind of makes sense, as I have this inheritance structure, which
> prevents it from creating the object, but this is exactly what I would
> like to understand better. Is inheritance preventing ResourceRef to be
> created until a fully valid Statement is available (line ending with ';') ?
> Can I keep the inheritance and still have the super objects created
> somehow? thx Christophe
>
>
>
|
|
| | | | |
Goto Forum:
Current Time: Sat Apr 27 05:01:28 GMT 2024
Powered by FUDForum. Page generated in 0.04294 seconds
|