Nested Struct with Qualified Name [message #1074230] |
Fri, 26 July 2013 06:24  |
Eclipse User |
|
|
|
Hello!
I'm trying to make C-like structs with xtext, but I face a problem with qualified names:
The struct IS a type, and a type can have another type as its type. (that sounds weird ^^)
So now i want to create a qualified name that looks something like this:
variable.substruct.subsubstruct.variable
x.struct1.struct2.y //for example
Example:
TYPE
koords : STRUCT
x : INT;
y : INT;
z : INT;
END_STRUCT;
koords2 : STRUCT
x : koords;
y : koords;
z : koords;
END_STRUCT;
END_TYPE
VAR PRIVATE
myWorld : koords;
myOtherWorld: koords2;
END_VAR
ROUTINE main
//Should not work
myWorld.koords.integer := 5;
myWorld.koords2.koords := 5;
//Should work
// myWorld.x :=5;
// myOtherWorld.x.x := 5;
END_ROUTINE
Grammar:
grammar org.xtext.example.mydsl.MyDsl with org.eclipse.xtext.common.Terminals
generate myDsl "..."
Model:
(elements+=AbstractElement)*
;
AbstractElement:
types += TypeBlock | routines += RoutineBlock | variables += VariableBlock
;
TypeBlock: {TypeBlock}
'TYPE'
(types+=Type)*
'END_TYPE'
;
Type: name=ID ':' (type=SimpleType | struct=Struct | typeRef=[Type]) ';';
Struct: {Struct}
'STRUCT'
(variables+=Variable)*
'END_STRUCT'
;
VariableBlock: {VariableBlock}
'VAR' (isGlobal?='GLOBAL' | isPrivate?='PRIVATE')
(variables+=Variable)*
'END_VAR'
;
Variable: name=ID ':' (typeRef=[Type] | type=SimpleType) ';';
RoutineBlock:
'ROUTINE' name=ID
(statements+=Statement)*
'END_ROUTINE'
;
Statement: DotExpression;
DotExpression returns Ref:
VariableRef ({DotExpression.ref=current} "." tail=[SuperRef])* ':=' INT ';'
;
VariableRef returns Ref:
{EntityRef} entity=[Variable]
;
SuperRef: Variable|Type;
//Allocation:
// ref=[SuperRef | QualifiedName] ':=' INT ';'
//;
//SuperRef: variable=Variable|type=Type;
//QualifiedName:
// ID ('.' ID)*
//;
SimpleType:
(IntType | BoolType | RealType)
;
IntType: {IntType} "INT";
BoolType: {BoolType} "BOOL";
RealType: {RealType} "REAL";
I tried it with [xxx | QualifiedName] and left factoring, but i just cant figure it out.
Thanks for your help!
|
|
|
|
|
|
|
Re: Nested Struct with Qualified Name [message #1707661 is a reply to message #1707560] |
Tue, 08 September 2015 08:11   |
Eclipse User |
|
|
|
Hi,
Making your example work required a bit of extra work to make things fit with Xtext workflow.
First, I simplified your grammar in order to keep only the minimal things necessary. I also refactored some elements so that it will simplify further explanations. Here is the grammar. One of the main things was to separate the "component" declarations inside a structure from variable declaration (as an AbstractElement). You can still merge them if necessary, since they parse the same.
grammar org.xtext.example.mydsl.MyDsl with org.eclipse.xtext.common.Terminals
generate myDsl "..."
Model:
(elements+=AbstractElement)*
;
AbstractElement:
types += TypeBlock | routines += RoutineBlock | variables += VariableBlock
;
TypeBlock: {TypeBlock}
'TYPE'
(types+=Structure)*
'END_TYPE'
;
Structure:
name=ID ':' 'STRUCT' (components+=StructureComponent)+ 'END_STRUCT'
;
StructureComponent:
name=ID ':' type=[Structure] |
name=ID ':' simple=PrimitiveType
;
VariableBlock: {VariableBlock}
'VAR' (isGlobal?='GLOBAL' | isPrivate?='PRIVATE')
(variables+=Variable)*
'END_VAR'
;
Variable: name=ID ':' type=[Structure] ';';
RoutineBlock:
'ROUTINE' name=ID
(statements+=Statement)*
'END_ROUTINE'
;
Statement: DotExpression;
DotExpression returns Expression:
VariableRef ({DotExpression.receiver=current} "." tail=[StructureComponent])* ':=' INT ';'
;
VariableRef returns Expression:
{VariableRef} ref=[Variable]
;
PrimitiveType:
{IntType} "INT" |
{BoolType} "BOOL" //|
// {RealType} "REAL"
;
The main point is to have a name for each relevant element (component of a structure, variable, etc.) so that we can rely on Xtext scoping/naming conventions.
Next, in order to enable your dot-based notation, we need to provide the proper scope for an expression like "var.x". The principle is based on retrieving the potential elements that can appear after the "." based on the type of the receiver var: if a "." is possible, then it means that var is a structure with a component named x.
So first, we need to type expressions. To make things simple, I just consider structures as types (you can extend that with SimpleType or more complicated things, once you get it, it's easier). So I created a MyDSLTypeProvider in the proper package (see package declaration in the code) that computes the type of an expression:
- The type of a VariableRef is the reference's declared type;
- The type of a DotExpression is the type of the tail, i.e. the type of the structure's component.
package org.xtext.example.mydsl.typing
import org.xtext.example.mydsl.myDsl.BoolType
import org.xtext.example.mydsl.myDsl.DotExpression
import org.xtext.example.mydsl.myDsl.Expression
import org.xtext.example.mydsl.myDsl.IntType
import org.xtext.example.mydsl.myDsl.MyDslFactory
import org.xtext.example.mydsl.myDsl.PrimitiveType
import org.xtext.example.mydsl.myDsl.Structure
import org.xtext.example.mydsl.myDsl.StructureComponent
import org.xtext.example.mydsl.myDsl.VariableRef
class MyDSLTypeProvider {
public static val integerType = MyDslFactory::eINSTANCE.createStructure => [name = "Integer"]
public static val booleanType = MyDslFactory::eINSTANCE.createStructure => [name = "Boolean"]
public static val nullType = MyDslFactory::eINSTANCE.createStructure => [name = "Null"]
def Structure typeFor(Expression e) {
switch (e) {
VariableRef: e.ref.type
DotExpression: e.tail.typeOf
}
}
def static typeOf(StructureComponent c){
if(c.type != null)
return c.type
else
return c.simple.typeOf
}
def static typeOf(PrimitiveType ptype){
switch(ptype){
IntType: integerType
BoolType: booleanType
}
}
// returns true if fakely created
// as a static value
def isPrimitive(Structure c) {
c.eResource == null
}
}
Note that we need "fake" structure types because your langage allows so-called PrimitiveType-s: I created one such "fake" structure for each necessary type (feel free to extend it in the future).
Now that we can type expressions, we can compute the scopes: we need to indicate to Xtext how to resolve the reference [StructureComponent] in a DotExpression like var.x. The component x has to be found between the components available for the type of var. In your example, since myWorld is declared as a koords, we indicate that the tail x should be among the structure components of koords. We create an Xtend class MyDslScopeProvider with a dedicated scope function to scope this particular component. We need to respect the Xtext convention for the name of the function: scope_DotExpression_tail.
Here is the corresponding code with all you need to make it work (please respect the correct package, the correct class extends clause, and so on):
package org.xtext.example.mydsl.scoping
import com.google.inject.Inject
import org.eclipse.emf.ecore.EReference
import org.eclipse.xtext.scoping.IScope
import org.eclipse.xtext.scoping.Scopes
import org.eclipse.xtext.scoping.impl.AbstractDeclarativeScopeProvider
import org.xtext.example.mydsl.myDsl.DotExpression
import org.xtext.example.mydsl.myDsl.Structure
import org.xtext.example.mydsl.typing.MyDSLTypeProvider
class MyDslScopeProvider extends AbstractDeclarativeScopeProvider{
@Inject extension MyDSLTypeProvider
def scope_DotExpression_tail(DotExpression e, EReference ref){
val nullScope = IScope::NULLSCOPE
val type = e.receiver.typeFor
if(type == null || type.isPrimitive)
return nullScope
if(type instanceof Structure)
return Scopes::scopeFor((type as Structure).components, nullScope)
}
}
With that, everything should work correctly: it parses, and it becomes clickable thanks to the proper scopes.
However, you should add some checks to ensure e.g. that components in the same structure (as well as structure/variable declarations) have different names, otherwise the clickable reference would become ambiguous.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Powered by
FUDForum. Page generated in 0.42806 seconds