Commit 36163b97 authored by Daniel Honsel's avatar Daniel Honsel
Browse files

some work on components:

- recognize name clashes in hierarchy
- scope for type extensions
- improved cross references for component types
- added scope test file
parent 3450af82
Loading
Loading
Loading
Loading
+14 −15
Original line number Diff line number Diff line
@@ -190,7 +190,7 @@ ConfigSpec:
	RunsOnSpec spec=SystemSpec?;

SystemSpec:
	SYSTEMKEYWORD ComponentType;
	SYSTEMKEYWORD component=[ComponentDef|IDENTIFIER];

SignatureDef:
	SIGNATUREKEYWORD name=IDENTIFIER LPAREN params=SignatureFormalParList? RPAREN (return=ReturnType | NOBLOCKKEYWORD)?
@@ -635,10 +635,10 @@ ReferencedType:
	SubTypeDefNamed | RecordDefNamed | SetDefNamed | PortDef | ComponentDef | UnionDefNamed | EnumDefNamed;

TypeDef:
	TYPEDEFKEYWORD TypeDefBody;
	TYPEDEFKEYWORD body=TypeDefBody;

TypeDefBody:
	StructuredTypeDef | SubTypeDef;
	structured=StructuredTypeDef | sub=SubTypeDef;

SubTypeDef:
	type=Type ADDRESSKEYWORD array=ArrayDef? spec=SubTypeSpec? | SubTypeDefNamed;
@@ -647,7 +647,8 @@ SubTypeDefNamed returns SubTypeDef:
	type=Type name=IDENTIFIER array=ArrayDef? spec=SubTypeSpec?;

StructuredTypeDef:
	RecordDef | UnionDef | SetDef | RecordOfDef | SetOfDef | EnumDef | PortDef | ComponentDef;
	record=RecordDef | union=UnionDef | set=SetDef | recordOf=RecordOfDef | setOf=SetOfDef | enumDef=EnumDef |
	port=PortDef | component=ComponentDef;

RecordDef:
	RECORDKEYWORD ADDRESSKEYWORD StructDefBody | RecordDefNamed;
@@ -719,12 +720,13 @@ ProcedureAttribs:
	PROCEDUREKEYWORD LBRACKET ((addresses+=AddressDecl | procs+=ProcedureList | configs+=ConfigParamDef) SEMICOLON?)+
	RBRACKET;

	// TODO: restrict extends type scope (do not allow self referencing)
ComponentDef:
	COMPONENTKEYWORD name=IDENTIFIER (EXTENDSKEYWORD ct+=ComponentType (COMMA ct+=ComponentType)*)? LBRACKET
	(defs=ComponentDefList) RBRACKET;
	COMPONENTKEYWORD name=IDENTIFIER (EXTENDSKEYWORD extends+=[ComponentDef|IDENTIFIER] (COMMA
	extends+=[ComponentDef|IDENTIFIER])*)? LBRACKET defs+=ComponentDefList* RBRACKET;

ComponentDefList:
	{ComponentDefList} (def+=ComponentElementDef wst+=WithStatement? sc+=SEMICOLON?)*;
	element=ComponentElementDef wst=WithStatement? sc=SEMICOLON?;

PortInstance:
	PORTKEYWORD name=ExtendedIdentifier element+=PortElement (COMMA element+=PortElement)*;
@@ -733,7 +735,7 @@ PortElement:
	name=IDENTIFIER array=ArrayDef?;

ComponentElementDef:
	PortInstance | VarInstance | TimerInstance | ConstDef;
	port=PortInstance | variable=VarInstance | timer=TimerInstance | const=ConstDef;

ProcedureList:
	Direction AllOrSignatureList;
@@ -920,7 +922,7 @@ ConfigurationOps:
	RunningOp;

CreateOp:
	{CreateOp} => (type=ComponentType DOT CREATEKEYWORD) (LPAREN (expr1=SingleExpression | MINUS) (COMMA
	{CreateOp} => (type=[ComponentDef|IDENTIFIER] DOT CREATEKEYWORD) (LPAREN (expr1=SingleExpression | MINUS) (COMMA
	expr2=SingleExpression)? RPAREN)? ALIVEKEYWORD?;

RunningOp:
@@ -1212,10 +1214,7 @@ FormalTemplatePar:
	name=IDENTIFIER (ASSIGNMENTCHAR (templ=InLineTemplate | MINUS))?;

RunsOnSpec:
	RUNSKEYWORD ONKEYWORD component=ComponentType;

ComponentType:
	ref=[ReferencedType|IDENTIFIER]; // TODO: restrict selectable types?
	RUNSKEYWORD ONKEYWORD component=[ComponentDef|IDENTIFIER];

ReturnType:
	RETURNKEYWORD (TEMPLATEKEYWORD | RestrictedTemplate)? type=Type;
+23 −2
Original line number Diff line number Diff line
@@ -6,6 +6,11 @@ import org.eclipse.xtext.scoping.IScope
import org.eclipse.xtext.scoping.impl.AbstractDeclarativeScopeProvider

import static extension de.ugoe.cs.swe.common.TTCN3ScopeHelper.*
import static org.eclipse.xtext.scoping.Scopes.*
import de.ugoe.cs.swe.tTCN3.ComponentDef
import de.ugoe.cs.swe.tTCN3.ModuleDefinitionsList
import de.ugoe.cs.swe.tTCN3.TypeDef
import de.ugoe.cs.swe.tTCN3.ModuleDefinition

class TTCN3ScopeProvider extends AbstractDeclarativeScopeProvider {

@@ -13,4 +18,20 @@ class TTCN3ScopeProvider extends AbstractDeclarativeScopeProvider {
		value.loopVariableScope
	}

	def IScope scope_ComponentDef_extends(ComponentDef component, EReference ref) {
		val container = component.findDesiredParent(typeof(ModuleDefinitionsList)) as ModuleDefinitionsList
		val inner = newArrayList
				
		for (ModuleDefinition d : container.definitions.filter[
			it.def != null && it.def instanceof TypeDef && (it.def as TypeDef).body.structured.component != null]) {
				
			val comp = (d.def as TypeDef).body.structured.component as ComponentDef
			
			if (component != comp) {
				inner.add(comp)				
			}			
		}
		scopeFor(inner)
	}

}
+56 −0
Original line number Diff line number Diff line
@@ -22,6 +22,8 @@ import org.eclipse.xtext.validation.Check
import de.ugoe.cs.swe.tTCN3.FormalValuePar
import de.ugoe.cs.swe.tTCN3.ModuleControlPart
import de.ugoe.cs.swe.tTCN3.ControlStatementOrDefList
import de.ugoe.cs.swe.tTCN3.ComponentDef
import de.ugoe.cs.swe.tTCN3.ComponentDefList

class TTCN3Validator extends AbstractTTCN3Validator {

@@ -40,6 +42,22 @@ class TTCN3Validator extends AbstractTTCN3Validator {
		variable.isNameClashInHierarchy
	}

	// TODO: adapt auto-completion (remove extended types)
	// TODO: treat cyclic dependencies??
	@Check
	def checkUniqueComponentExtension(ComponentDef component) {
		var ComponentDef last = null;
		var ComponentDef current = null;
		for (ComponentDef d : component.extends.sortBy[it.name]) {
			current = d;
			if (current == last) {
				error('The extension list contains the type ' + current.name + ' twice!', null, -1);
				return;
			}
			last = current
		}
	}

	def private isNameClashInHierarchy(RefValue variable) {
		var parent = variable.eContainer
		while (parent != null) {
@@ -81,6 +99,11 @@ class TTCN3Validator extends AbstractTTCN3Validator {
					error('Name clash with another variable definition in this scope!', null, -1);
					return;
				}
			} else if (parent instanceof ComponentDef) {
				if (!parent.checkNameClashComponentDefHierarchy(variable)) {
					error('Name clash with another variable definition in this scope!', null, -1);
					return;
				}
			}

			// TODO: other references in scope
@@ -169,6 +192,39 @@ class TTCN3Validator extends AbstractTTCN3Validator {
		return false;
	}

	// this method returns true if no name clash occurs and false otherwise
	// the behavior is required by the calling method checkNameClashComponentDefHierarchy
	def private boolean checkNameClashComponentDef(ComponentDef component, RefValue variable) {
		for (ComponentDefList l : component.defs) {
			if (l.element.const != null) {
				val constDefList = l.element.const.defs as ConstList
				for (SingleConstDef d : constDefList.list) {
					if (d != variable && d.name == variable.name) {
						return false;
					}
				}
			}
			if (l.element.variable != null) {
				val varList = l.element.variable.list as VarList
				for (SingleVarInstance d : varList.variables) {
					if (d != variable && d.name == variable.name) {
						return false;
					}
				}
			}
		}
		return true;
	}

	def private boolean checkNameClashComponentDefHierarchy(ComponentDef component, RefValue variable) {
		var boolean res = component.checkNameClashComponentDef(variable)

		for (ComponentDef d : component.extends)
			res = res && d.checkNameClashComponentDefHierarchy(variable)

		return res;
	}

	@Check
	def checkModuleStartsWithM(TTCN3Module module) {
		val char pattern = 'M';