@=~ ~p typesetter = html ~p maximum_input_line_length = infinity ~t title titlefont left "Ein universeller Graph-Editor" In diesem Beispiel wird eine Editor spezifiziert, mit dem man beliebige Graphen konstruieren kann. Es kann zwischen gerichteten und ungerichteten Kanten umgeschaltet werden. Der Editor unterstuetzt zwei verschiedene Knotentypen, deren Aussehen jeweils konfigurierbar ist. Die Spezifikation ist folgendermassen strukturiert: ~t table_of_contents ~A~ ~B~ Zunaechst muss die abstrakte Struktur der Sprache spezifizert werden, auf der alle anderen Spezifikationen basieren. Hierzu wird eine flache Baumstruktur verwendet mit Querbeziehungen und persistenten Attributen der Baumknoten verwendet. Das Ganze wird in einer Datei namens "graph.vlag" spezifiziert. ~O~~{ ~ ~} Graphen bestehen aus einer Knoten- und einer Kantenmenge. Ausserdem sind ihnen Parametrisierungsoptionen zugeordnet: Man kann zwischen gerichteten und ungerichteten Kanten waehlen und das Aussehen der zwei verschiedenen Knotentypen bestimmten. ~$~+=~{ VLROOT Root(Graph(NodeList(Node*), EdgeList(Edge*))) { PERS GraphType: VLInt EDITWITH OPTION ("directed","undirected"); PERS SmoothLines: VLBoolean EDITWITH CHECKBOX; PERS NodeType1Color: VLString EDITWITH COLOR; PERS NodeType1Shape: VLInt EDITWITH OPTION ("rectangle","rounded-rectangle","circle"); PERS NodeType2Color: VLString EDITWITH COLOR; PERS NodeType2Shape: VLInt EDITWITH OPTION ("rectangle","rounded-rectangle","circle"); } ~} Es gibt zwei verschiedene Knotentypen. Jeder Knoten hat ein persistentes Attribut, das den Namen des Knotens speichert. Zusaetzlich gibt es den Definitionstabellenzeiger ~{Endpoint~}. Dieser gibt an, welche Linien an einem Knoten enden. ~$~+=~{ ABSTRACT Node {} VLRULE NodeType1() ACTS AS Node { PERS Name: VLString EDITWITH ENTRY; PERS Endpoint: DEFTABLEKEY; } VLRULE NodeType2() ACTS AS Node { PERS Name: VLString EDITWITH ENTRY; PERS Endpoint: DEFTABLEKEY; } ~} Jetzt fehlen noch die Kanten: Jeder Kante hat eine Beschriftung und ein Start- und Zielknoten zugeordnet. Das untergeordnete Baumsymbol ~{EdgeLabel~} dient zum Layout der Beschriftung. ~$~+=~{ VLRULE Edge(EdgeLabel()) { PERS Name: VLString EDITWITH ENTRY; PERS From: DEFTABLEKEY; PERS To: DEFTABLEKEY; } ~} ~B~ Nachfolgend werden die initialen Werte der persistente Attribute definiert. Dazu wird ein allgemeineres Konzept benutzt: Es gibt einen speziellen Attributauswerter, der beliebige Aenderungen an der abstrakten Struktur durchfuehren kann. Waehrend des Attributauswerterdruchlaufs wird auch das Setzen von Defaultwerten durchgefuehrt. ~O~~{ #ifndef BASIC_CHECK_LIDO_MACROS_H #define BASIC_CHECK_LIDO_MACROS_H #ifdef __STDC__ #define STR(attr) #attr #else #define STR(attr) "attr" #endif #define VL_SET_DEFAULT_VALUE(ATTRNAME,VALUE) \ IF(SELECT(THIS.ATTRNAME,isNull()), \ PTGBasicCheckExecute(2,PTGSetAttr(_currn,STR(ATTRNAME),VALUE))) #endif SYMBOL Root COMPUTE VL_SET_DEFAULT_VALUE(persNodeType1Shape,"0"); VL_SET_DEFAULT_VALUE(persNodeType2Shape,"2"); VL_SET_DEFAULT_VALUE(persNodeType1Color,"black"); VL_SET_DEFAULT_VALUE(persNodeType2Color,"black"); VL_SET_DEFAULT_VALUE(persGraphType,"0"); VL_SET_DEFAULT_VALUE(persSmoothLines,"0"); END; ~} ~A~ Zur Berechnung der visuellen Darstellung benutzen wir einen eigenen Attributauswerter, der hier ~{view0~} genannt ist. Dies soll darauf hindeuten, dass es i.a. noch andere Sichten auf die abstrakte Struktur geben koennte. ~O~~{ ~ ~} Zur Benutzung der Spezifikationsmodule fuer die visuellen Muster muessten diese instanziiert und dem Attributauswerter ~{view0~} zugeordnet werden. ~O~~{ $/Visual/VisPattern2.gnrc+instance=view0:inst $/Visual/StdDrawings.fw $/Visual/VisConnectionPattern2Level.gnrc+instance=1+referto=view0:inst ~} ~B~ Knoten vom Typ 1 und Knoten vom Typ 2 koennen ueber weite Strecken gleich behandelt werden. Daher werden diese durch eine Klassensymbol zusammengefasst und Unterschiede herausfaktorisiert. ~$~+=~{ CLASS SYMBOL NodeTypeClass: NodeTypeShape: int, NodeTypeColor, FrameDrawingAttr: VLCharPtr; SYMBOL NodeType1 INHERITS NodeTypeClass COMPUTE INH.NodeTypeShape = INCLUDING Root.persNodeType1Shape; INH.NodeTypeColor = INCLUDING Root.persNodeType1Color; END; SYMBOL NodeType2 INHERITS NodeTypeClass COMPUTE INH.NodeTypeShape = INCLUDING Root.persNodeType2Shape; INH.NodeTypeColor = INCLUDING Root.persNodeType2Color; END; ~} ~B~ Zur Visualisierung der Baumstruktur wenden wir das Mengenmuster an: Der Knoten ~{Graph~} spielt die Rolle einer (visuellen) Menge, Knoten der Klasse ~{NodeTypeClass~} (also ~{NodeType1~} und ~{NodeType2~}) repraesentieren konkrete Mengenelemente. ~$~+=~{ SYMBOL Graph INHERITS VP_Set COMPUTE THIS.VP_OptStartSize=VisIntVector(600,500); THIS.VP_OptFrameStyle = "-fill {} -outline {}"; THIS.VP_OptContainerKind = 1; END; SYMBOL _List_Node INHERITS VP_ListContext END; SYMBOL Node INHERITS VP_MovableContext END; SYMBOL NodeTypeClass INHERITS VP_Movable END; ~} ~B~ Zur Beschreibung der Darstellung von Knoten wird die Symbolrolle ~{TextTerminal~} verwendet. Kern der Darstellung ist eine beliebige Zeichenkette. Durch das Attribut ~{OptFrameDrawing~} kann ein Rahmen um den Text spezifiziert werden. In diesem Fall haengt das Aussehen des Rahmens von den Konfiguartionseinstellungen im Graph-Kontext ab. ~$~+=~{ SYMBOL NodeTypeClass INHERITS VP_TextTerminal COMPUTE SYNT.VP_OptText = THIS.persName; SYNT.VP_OptFont = "default"; SYNT.VP_OptTextAttr = "persName"; SYNT.FrameDrawingAttr = VLString(VLList("-outline",THIS.NodeTypeColor, VLList("-fill {} -width 2"))); SYNT.VP_OptDefault = ""; SYNT.VP_OptFrameDrawing = IF( EQ(THIS.NodeTypeShape,0), NEW(FrameDrawing(THIS.FrameDrawingAttr,5,VisIntVector(20,20))), IF( EQ(THIS.NodeTypeShape,1), NEW(RoundedFrameDrawing(THIS.FrameDrawingAttr,5, 20)), NEW(CircleFrameDrawing(THIS.FrameDrawingAttr,5,VisIntVector(20,20))) ) ); END; ~} ~B~ Um Baumsymbole des Typs ~{Edge~} als Linien darzustellen, wird das Linienmuster verwendet: Baumknoten vom Typ ~{Edge~} spielen die Rolle der Linienverbindung (~{RelationTypeDirect~}), Baumsymbole der Klasse ~{NodeTypeClass~} spielen die Rolle von (potentiellen) Linienendpunkten. Der Knoten ~{Graph~} repraesentiert den Gueligkeitsbereich der Linienmenge. ~$~+=~{ SYMBOL Graph INHERITS VP_RelationArea END; SYMBOL _List_Edge INHERITS VP_ListSimpleRelations END; SYMBOL NodeTypeClass INHERITS VP_EndPoint, VP_EndPointLevel1 COMPUTE SYNT.VP_OptToTheEnd = 0;/* Linien enden am Rand */ SYNT.VP_OptShape = IF(EQ(THIS.NodeTypeShape,2),1,0); END; SYMBOL Edge INHERITS VP_RelationTypeDirect, VP_RelationLevel1 COMPUTE SYNT.VP_OptLineType = VLString(VLList("-smooth", INCLUDING Root.persSmoothLines,VLList("-width 2"))); SYNT.VP_OptArrowType = IF(EQ(INCLUDING Root.persGraphType,0),VLCharPtr("-arrow last"),VLCharPtr("-arrow none")); /* without cast, there are some problems with "const char *" */ END; ~} ~B~ Das Symbol ~{EdgeLabel~} repraesentiert die Beschriftung von Linien. Dazu erbt es von der Klasse ~{RelationLabel2~}. Zur Definition des Aussehens von Beschriftungen wird die Symbolrolle ~{TextTerminal2~} verwendet. ~$~+=~{ SYMBOL EdgeLabel INHERITS VP_RelationLabel2 END; SYMBOL EdgeLabel INHERITS VP_TextTerminal2 COMPUTE SYNT.VP_OptDefault = ""; SYNT.VP_OptText = INCLUDING Edge.persName; END; ~} ~B~ Aus technischen Gruenden ist es notwendig, die in Lido spzifizierten Vererbungen zusaetzlich in Vtree anzugeben. Die nachfolgenden Zeilen koennten automatisch aus der Lido-Spezifikation hergeleitet werden. ~O~~{ SYMBOL Graph INHERITS VP_view0_Set END; SYMBOL Node INHERITS VP_view0_MovableContext END; SYMBOL Edge INHERITS VP_view0_RelationType END; SYMBOL EdgeLabel INHERITS VP_view0_RelationLabel2 END; SYMBOL NodeType1 INHERITS VP_view0_EndPoint END; SYMBOL NodeType2 INHERITS VP_view0_EndPoint END; ~}