Upload
buidien
View
214
Download
0
Embed Size (px)
Citation preview
Universita degli Studi di Udine
Facolta di Scienze Matematiche Fisiche e Naturali
Corso di Laurea Triennale in Informatica
Tesi di Laurea
Un ambiente di sviluppoper bigrafi diretti
Candidato:
Patrik Osgnach
Relatore:
prof. Marino Miculan
Anno Accademico 2007/2008
Universita degli Studi di UdineVia delle Scienze, 206
33100 UdineItalia
Indice
1 Introduzione 1
2 Bigrafi diretti 7
2.1 Place graph . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
2.1.1 Definizioni . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
2.2 Directed link graph . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
2.2.1 Definizioni . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
2.3 Bigrafi diretti . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
2.3.1 Algebra . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
2.3.2 Bigrafi elementari . . . . . . . . . . . . . . . . . . . . . . . . 16
3 Implementazione dei bigrafi diretti 19
3.1 Linguaggio utilizzato . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
3.2 Struttura dati . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
3.2.1 Insieme delle signature . . . . . . . . . . . . . . . . . . . . . . 20
3.2.2 Classe directedbg . . . . . . . . . . . . . . . . . . . . . . . . . 20
3.2.2.1 Insieme dei nodi . . . . . . . . . . . . . . . . . . . . 22
3.2.2.2 Insieme degli archi . . . . . . . . . . . . . . . . . . . 22
3.2.2.3 Place graph . . . . . . . . . . . . . . . . . . . . . . . 22
3.2.2.4 Link graph . . . . . . . . . . . . . . . . . . . . . . . 23
3.2.2.5 Lista dei controlli . . . . . . . . . . . . . . . . . . . 24
3.2.2.6 Interfaccia interna . . . . . . . . . . . . . . . . . . . 24
3.2.2.7 Interfaccia esterna . . . . . . . . . . . . . . . . . . . 25
3.3 Operazioni di base . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
3.3.1 Tensore . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
3.3.2 Composizione . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
3.3.2.1 Insieme dei nodi . . . . . . . . . . . . . . . . . . . . 26
3.3.2.2 Insieme degli archi . . . . . . . . . . . . . . . . . . . 27
iv INDICE
3.3.2.3 Place graph . . . . . . . . . . . . . . . . . . . . . . . 27
3.3.2.4 Link graph . . . . . . . . . . . . . . . . . . . . . . . 27
3.3.2.5 Lista dei controlli . . . . . . . . . . . . . . . . . . . 28
3.3.2.6 Interfaccia interna . . . . . . . . . . . . . . . . . . . 28
3.3.2.7 interfaccia esterna . . . . . . . . . . . . . . . . . . . 28
3.3.2.8 Il metodo composition . . . . . . . . . . . . . . . . . 28
3.4 Operazioni derivate . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
3.4.1 Sharing products . . . . . . . . . . . . . . . . . . . . . . . . . 29
3.4.1.1 Outer sharing product . . . . . . . . . . . . . . . . . 29
3.4.1.2 Inner sharing product . . . . . . . . . . . . . . . . . 29
3.4.1.3 Sharing product . . . . . . . . . . . . . . . . . . . . 30
3.4.2 Prime sharing products . . . . . . . . . . . . . . . . . . . . . 30
3.4.2.1 Prime outer sharing product . . . . . . . . . . . . . 31
3.4.2.2 Prime sharing product . . . . . . . . . . . . . . . . . 31
3.5 Algebra . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
3.5.1 Bigrafi elementari . . . . . . . . . . . . . . . . . . . . . . . . 32
3.5.1.1 Swap . . . . . . . . . . . . . . . . . . . . . . . . . . 32
3.5.1.2 Identita . . . . . . . . . . . . . . . . . . . . . . . . . 32
3.5.1.3 Closure . . . . . . . . . . . . . . . . . . . . . . . . . 33
3.5.1.4 Up-closure . . . . . . . . . . . . . . . . . . . . . . . 33
3.5.1.5 Down-closure . . . . . . . . . . . . . . . . . . . . . . 34
3.5.1.6 Merge . . . . . . . . . . . . . . . . . . . . . . . . . . 34
3.5.1.7 Barren root . . . . . . . . . . . . . . . . . . . . . . . 34
3.5.1.8 Substitution . . . . . . . . . . . . . . . . . . . . . . 35
3.5.1.9 Empty Substitution . . . . . . . . . . . . . . . . . . 35
3.5.1.10 Fusion . . . . . . . . . . . . . . . . . . . . . . . . . . 35
3.5.1.11 Empty Fusion . . . . . . . . . . . . . . . . . . . . . 35
3.5.1.12 Rename . . . . . . . . . . . . . . . . . . . . . . . . . 36
3.5.1.13 Discrete Ion . . . . . . . . . . . . . . . . . . . . . . 36
3.5.2 Forma normale discreta . . . . . . . . . . . . . . . . . . . . . 37
3.5.2.1 Scomposizione in bigrafi primi . . . . . . . . . . . . 40
3.5.3 Uguaglianza tra bigrafi . . . . . . . . . . . . . . . . . . . . . . 40
INDICE v
4 Il linguaggio ed il compilatore 43
4.1 Il linguaggio . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43
4.1.1 La grammatica . . . . . . . . . . . . . . . . . . . . . . . . . . 43
4.2 Il Compilatore . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45
4.2.1 L’analizzatore lessicale . . . . . . . . . . . . . . . . . . . . . . 46
4.2.2 Il parser . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47
4.2.2.1 La signature . . . . . . . . . . . . . . . . . . . . . . 47
4.2.2.2 I bigrafi elementari . . . . . . . . . . . . . . . . . . . 48
4.2.2.3 Le operazioni tra bigrafi . . . . . . . . . . . . . . . . 54
4.2.2.4 Forzare le precedenze . . . . . . . . . . . . . . . . . 62
4.2.2.5 I blocchi let . . . . . . . . . . . . . . . . . . . . . . . 62
4.2.3 L’interfaccia del compilatore . . . . . . . . . . . . . . . . . . 62
4.3 Aspetti implementativi . . . . . . . . . . . . . . . . . . . . . . . . . . 63
4.3.1 L’analizzatore lessicale . . . . . . . . . . . . . . . . . . . . . . 63
4.3.1.1 Intestazione . . . . . . . . . . . . . . . . . . . . . . . 63
4.3.1.2 Definizioni . . . . . . . . . . . . . . . . . . . . . . . 64
4.3.1.3 Regole . . . . . . . . . . . . . . . . . . . . . . . . . 65
4.3.1.4 Differenze per la generazione del programma OCaml 66
4.3.2 Il Parser . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66
4.3.2.1 Intestazione . . . . . . . . . . . . . . . . . . . . . . . 67
4.3.2.2 Dichiarazioni . . . . . . . . . . . . . . . . . . . . . . 69
4.3.2.3 Regole . . . . . . . . . . . . . . . . . . . . . . . . . 70
4.3.2.4 Differenze per la generazione del programma ocaml 77
4.3.3 L’interfaccia del compilatore . . . . . . . . . . . . . . . . . . 77
5 Il decompilatore 79
5.1 Decompilazione della signature . . . . . . . . . . . . . . . . . . . . . 79
5.2 Decompilazione del bigrafo . . . . . . . . . . . . . . . . . . . . . . . 80
5.2.1 Componente w . . . . . . . . . . . . . . . . . . . . . . . . . . 80
5.2.2 Componente d . . . . . . . . . . . . . . . . . . . . . . . . . . 81
5.3 Funzioni ausiliarie . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83
5.4 Esempo di output . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85
vi INDICE
6 Il visualizzatore 87
6.1 Funzione principale . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87
6.2 Codice per il place graph . . . . . . . . . . . . . . . . . . . . . . . . 88
6.3 Posizioni di archi e nomi . . . . . . . . . . . . . . . . . . . . . . . . . 89
6.4 Codice per i link . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91
6.5 Altre funzioni . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 92
6.6 Esempio di output . . . . . . . . . . . . . . . . . . . . . . . . . . . . 96
7 Conclusioni 97
A Sorgenti del compilatore 99
A.1 Generazione delle coppie (signature,bigrafo) . . . . . . . . . . . . . . 99
A.1.1 Analizzatore lessicale . . . . . . . . . . . . . . . . . . . . . . . 99
A.1.2 Parser . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 100
A.2 Generazione del programma OCaml . . . . . . . . . . . . . . . . . . 107
A.2.1 Analizzatore lessicale . . . . . . . . . . . . . . . . . . . . . . . 107
A.2.2 Parser . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 109
A.3 Interfaccia del compilatore . . . . . . . . . . . . . . . . . . . . . . . . 116
B Sorgenti Decompilatore 117
C Sorgenti del visualizzatore 125
Bibliografia 139
1Introduzione
Tra i modelli per rappresentare processi e definirne il comportamento i “labelled
transition systems” (LTS) sono molto importanti. Essi specificano le capacita di
interazione di ogni componente tramite “etichette”, che rappresentano cosa puo
essere visto dall’ambiente. Molte metodologie per valutare equivalenze e verificare
proprieta dei processi quali la bisimulazione e il model checking si basano su modelli
di questo tipo.
La difficolta nell’usare questo modello consiste nel fatto che non ci sono metodi
“automatici” per crearli. Infatti gli LTS vengono in genere “fatti a mano” ma la
complessita di questi modelli cresce all’aumentare della complessita dei processi da
rappresentare.
Per ovviare a queste difficolta si e cercato un metodo per derivare gli LTS a
partire da modelli che possono descrivere processi ma che sono piu semplici da ge-
nerare. Purtroppo, in generale, i modelli che riescono a derivare degli LTS, dovendo
rappresentare tutte le interazioni di tutte le etichette in tutti i contesti, generano
LTS molto “grandi” e quindi poco usabili.
Un meta-modello emergente e quello dei bigrafi, descritto da Milner in [7, 8].
Questo modello e importante in quanto supporta un metodo per generare un’insieme
di etichette minimale e dunque generare degli LTS migliori. Questo metodo di
riduzione e l’RPO ovvero relative pushout ed una costruzione per i bigrafi e stata
data da Jensen e Milner in [6]. Un bigrafo e un meta-modello dove sia la localita
che la connettivita sono importanti, e composto da due strutture ortogonali, un
place graph per rappresentare la localita ed un link graph per rappresentare le
connessioni. Con questo tipo di modello si possono rappresentare modelli di calcolo
come il π-calcolo e le reti di Petri.
Un place graph rappresenta le localita, e composto da radici, nodi e siti. Esso
pua essere visto come una foresta di alberi. Ogni albero della foresta e generato da
2 CAPITOLO 1. INTRODUZIONE
L
K
L
L
K
x y
Figure 1: An example of a bigraph
1 IntroductionBigraphical reactive systems (BRSs) [28, 29, 30, 20] are a graphical model of compu-tation in which both locality and connectivity are prominent. Recognising the increas-ingly topographical quality of global computing, they take up the challenge to base alldistributed computation on graphical structure. A typical bigraph is shown in Figure 1.Such a graph is reconfigurable, and its nodes (the ovals and circles) may represent agreat variety of computational objects: a physical location, an administrative region, adata constructor, a !-calculus input guard, an ambient, a cryptographic key, a message,a replicator, and so on.Bigraphs are a development of action calculi [26], but simpler. They use ideas from
many sources: the Chemical Abstract machine (Cham) of Berry and Boudol [2], the!-calculus of Milner, Parrow and Walker [31], the interaction nets of Lafont [22], themobile ambients of Cardelli and Gordon [7], the explicit fusions of Gardner and Wis-chik [16] developed from the fusion calculus of Parrow and Victor [33], Nomadic Pictby Wojciechowski and Sewell [41], and the uniform approach to a behavioural theoryfor reactive systems of Leifer and Milner [24]. This memorandum is self-contained;it builds on preliminary definitions and results put forward by Milner [29], but theapproach here is a lot simpler and developed more fully.The theory of BRSs responds to twin challenges: from application, and from exist-
ing process theory. The former demands greater breadth of concepts, while the latterdemands continuity of ideas. We now discuss these challenges separately.
6
Figura 1.1: Esempio di bigrafo
una radice che come figli puo avere siti o nodi. I siti sono sempre foglie quindi non
possono avere figli, essi rappresentano punti in cui e possibile comporre due place
graph. La composizione di due place graph opera innestando una radice di uno in
un sito dell’altro.
Un nodo puo essere figlio di una radice oppure di un altro nodo e puo contenere
(avere come figli) dei siti.
L’altra struttura che fa parte dei bigrafi e il link graph: una struttura che de-
finisce le connessioni dei nodi alle risorse. Le risorse possono appartenere ad altri
nodi o essere esterne al bigrafo, raggiungibili passando per delle interfacce. Queste
interfacce contengono nomi che le rappresentano risorse esterne.
Place graph e link graph condividono l’insieme dei nodi ma per il resto sono
indipendenti.
Un esempio di bigrafo a rappresentato nella figura 1.1, mentre nelle figura 1.2 si
puo vedere come un bigrafo puo essere scomposto in un place graph ed un link graph.
Questo tipo di bigrafi possono essere modificati per disporre di uno strumento
ancora piu generale. Il modello in questione e quello dei bigrafi diretti introdotti da
Grohmann e Miculan in [4].
I bigrafi diretti mantengono la stessa struttura delle localita dei bigrafi di Milner,
3
Bigrafo G : 〈3, X〉 → 〈2, Y 〉
X = {x0, x1}; Y = {y0, y1, y2}Y = {y0, y1, . . .}X = {x0, x1, . . .}
roots . . . . . . names . . .
inner names . . . . . .sites . . .
link graph GL : X "Yplace graph GP : 3" 2
bigraph G : #3, X$"#2, Y $
r1
v3
v2
v0
r0
v1
v0
v2
v3
v1
v0
v1
v2
v3
r0 r1
s1 s2
y0 y1 y2
s1
y0 y1
x0
s2
y2
x1
s0
s0
x0 x1
Figure 8: Resolving a pure bigraph into a place graph and a link graph
pure bigraph as a combination of two independent mathematical structures — a placegraph and a link graph. Note that this combination is quite distinct from the categoricalcomposition used to insert one bigraph into another (e.g. an agent into a context). But itis simply related to them; to compose two bigraphs categorically, we first resolve theminto their respective place graphs and link graphs, then compose these, and finallycombine the results into a new bigraph.It is helpful to see an example in Figure 8 of how a pure bigraph G can be resolved
into a place graph GP representing locality, and a link graph GL representing connec-tivity. (Controls are not shown in the diagram.) The nodes v0, . . . , v3 are commonto the two structures, which are otherwise independent. Note the bigraph’s interfaces#3,X$"#2, Y $, which are pairs; there is no middle component here, because a purebigraph has no local names. This interface combines the place graph interface 3" 2with the link graph interfaceX"Y ; nothing determines that the names y0, y1, y2 ‘be-long’ to any particular region of the bigraph (= root of the place graph), nor that the
19
Place graph GP : 3→ 2 e link graph GL : X → Y
Y = {y0, y1, . . .}X = {x0, x1, . . .}
roots . . . . . . names . . .
inner names . . . . . .sites . . .
link graph GL : X "Yplace graph GP : 3" 2
bigraph G : #3, X$"#2, Y $
r1
v3
v2
v0
r0
v1
v0
v2
v3
v1
v0
v1
v2
v3
r0 r1
s1 s2
y0 y1 y2
s1
y0 y1
x0
s2
y2
x1
s0
s0
x0 x1
Figure 8: Resolving a pure bigraph into a place graph and a link graph
pure bigraph as a combination of two independent mathematical structures — a placegraph and a link graph. Note that this combination is quite distinct from the categoricalcomposition used to insert one bigraph into another (e.g. an agent into a context). But itis simply related to them; to compose two bigraphs categorically, we first resolve theminto their respective place graphs and link graphs, then compose these, and finallycombine the results into a new bigraph.It is helpful to see an example in Figure 8 of how a pure bigraph G can be resolved
into a place graph GP representing locality, and a link graph GL representing connec-tivity. (Controls are not shown in the diagram.) The nodes v0, . . . , v3 are commonto the two structures, which are otherwise independent. Note the bigraph’s interfaces#3,X$"#2, Y $, which are pairs; there is no middle component here, because a purebigraph has no local names. This interface combines the place graph interface 3" 2with the link graph interfaceX"Y ; nothing determines that the names y0, y1, y2 ‘be-long’ to any particular region of the bigraph (= root of the place graph), nor that the
19
Figura 1.2: Un bigrafo scomposto in place graph e link graph
4 CAPITOLO 1. INTRODUZIONE
Directed link graph A : ({u}, {s, t, w})→ ({z}, {x, y})x y z
w s t u
e0e1 v0
v1
Figura 1.3: Esempio di directed link graph
ovvero il place graph, pero il link graph e sostituito da un directed link graph che e
una sua generalizzazione.
L’idea chiave dei directed link graph e di notare come i nomi non sono risorse
ma solo un modo per indicare risorse esterne. Le risorse sono rappresentate da ar-
chi, rappresentati come oggetti espliciti e non come iper-archi, i nomi e le porte del
bigrafo diretto sono quindi collegati agli archi con archi semplici. In questo caso le
richieste partono dalle porte dei nodi,ed arrivano ad archi interni oppure su nomi
delle interfacce. Nei directed link graph pero un bigrafo puo offrire risorse attraverso
la stessa interfaccia quindi ci possono essere link, che rappresentano richieste ester-
ne, che dai nomi delle interfacce sono mappati su archi interni. Questo permette
di rappresentare flussi di richeste piu generali ovvero le risorse possono sia essere
richieste che fornite attraverso la stessa interfaccia.
Per evitare inconsistenze bisogna pero distinguere la polarita dei nomi di ogni
interfaccia seconda della direzione delle richieste: “verso l’alto” oppure “verso il
basso”.
La figura 1.3 mostra un esempio di directed link graph.
Anche per i bigrafi diretti e stata data una costruzione degli RPO [4] , rendendoli
dunque un modello valido per derivare LTS. Assieme a questo e stata sviluppata
un’algebra ed una forma normale in [3].
Ora, avendo a disposizione la teoria dei bigrafi diretti, e necessario realizzare
degli strumenti che permettano l’uso concreto di questo modello. La realizzazio-
ne degli strumenti deve tenere in considerazione chi saranno i possibili utenti degli
strumenti stessi. Fin da subito possiamo individuare come utenti i programmato-
5
ri di linguaggi concorrenti e le persone interessate alla formalizzazione di sistemi
concorrenti. Queste categorie di utenti hanno esigenze e competenze diverse: i pro-
grammatori preferiscono usare direttamente le strutture dati e le funzioni che queste
offrono mentre chi non e un esperto preferisce lavorare con interfacce intuitive e lin-
guaggi semplici da imparare ed usare. In pratica, per raggiungere questo obiettivo,
e necessario avere a disposizione:
1. Una implementazione del modello dei bigrafi diretti
2. Un insieme di funzioni per manipolare i bigrafi stessi
3. Un linguaggio per scrivere in modo semplice e veloce i bigrafi
4. Un compilatore ed un decompilatore per questo linguaggio
5. Un visualizzatore per poter vedere graficamente come e fatto un bigrafo
6. Una interfaccia grafica per “disegnare” i bigrafi in modo semplice
Attualmente abbiamo a disposizione: l’implementazione del modello e delle fun-
zioni realizzate da Pellarini in [9] e un linguaggio per i bigrafi (puri, non diretti)
realizzato presso l’ITU di Copenhagen [2]
Lo scopo di questo lavoro e di realizzare gli strumenti sopra descritti, in parti-
colare il linguaggio, il compilatore, il decompilatore e il visualizzatore. Il linguaggio
e un linguaggio simile ad un linguaggio di programmazione funzionale e consente
di scrivere bigrafi qualsiasi sotto forma di bigrafi elementari ed operazioni su di es-
si. Il compilatore e stato realizzato usando gli strumenti ocamllex e ocamlyacc che
generano rispettivamente un analizzatore lessicale ed un parser scritti in linguaggio
OCaml. Il compilatore fornisce un insieme di funzioni di interfaccia per essere usa-
to dagli utenti. Il decompilatore permette di ottenere una rappresentazione di un
qualsiasi bigrafo nel linguaggio precedentemente definito. Il visualizzatore permette
di “vedere” graficamente un bigrafo sotto forma di immagine SVG.
Questo volume e cosı suddiviso: Il capitolo 2 presenta la teoria dei bigrafi diretti,
le operazioni su di essi e l’algebra cosı come introdotti da Grohmann e Miculan in
[4, 3]. Il capitolo 3 descrive l’implementazione del modello e delle sue operazioni
realizzato da Pellarini in [9]. Il capitolo 4 descrive il linguaggio per bigrafi diretti, la
sua grammatica e sintassi e il funzionamento del compilatore. Il capitolo 5 descrive
il decompilatore e infine 6 descrive il visualizzatore. Infine gli appendici mostrano i
sorgenti degli strumenti realizzati.
2Bigrafi diretti
I bigrafi diretti si ottengono componendo un place graph ed un directed link graph
costruiti sugli stessi nodi.
2.1 Place graph
Si introducono in questa sezione i place graph come definiti in [5].
2.1.1 Definizioni
Definizione 1 (place graph). Un place graph A = (V, ctrl, prnt) : m → n ha
un’ampiezza interna m ed un’ampiezza esterna n, entrambi ordinali finiti; un insieme
finito di nodi V con una mappa di controlli ctrl : V → K; ed una parent map
prnt : m ] V → V ] n. La parent map e aciclica , ad es. prntk(v) 6= v per ogni
k > 0 e v ∈ V . Un nodo atomico - ovvero un nodo il cui controllo e atomico - non
puo essere padre. Scriveremo w >A w′, o semplicemente w > w′ per w = prntk(v)
per qualche k > 0.
Le ampiezze m ed n indicano il numero di siti e radici di A rispettivamente. I
siti e nodi - ovvero il dominio di prnt - sono chiamati place.
La condizione di aciclicita fa in modo che la parent map prnt rappresenti una
foresta di n alberi non ordinati. Siti e radici forniscono un metodo per comporre le
foreste di due place graph; ogni radice del primo e innestata in un differente sito del
secondo.
Definizione 2 (composizione). La composizione A1 ◦ A0 : m0 → m2 di due place
graph Ai = (Vi, ctrli, prnti) : mi → mi+1(i = 0, 1) e definita quando i due insiemi
dei nodi sono disgiunti; allora A1 ◦ A0 , (V, ctrl, prnt) dove V = V0 ] V1, ctrl =
ctrl0 ] ctrl1 e prnt = (IdV0 ] prnt1) ◦ (prnt0 ] IdV1). Il place graph identita di grado
m e Idm , (∅, ∅K, Idm) : m→ m.
8 CAPITOLO 2. BIGRAFI DIRETTI
E intuitivo che A ◦ Id = A = Id ◦A, e che la composizione e associativa.
Definizione 3 (barren, sibling, attivo, passivo). Un nodo o una radice e barren
se non ha figli. Due place sono sibling se hanno lo stesso padre. Un sito s di A e
attivo se ctrl(v) e attivo, ovvero se v > s; altrimenti s e passivo. Se s e attivo (risp.
passivo) in A, diciamo che A e attivo (risp. passivo) in s.
Quando si ha a che fare con molti place graph A,B, . . . invece di indicizzare
le loro parent map come prntA, prntB etc., troveremo piu comodo abusare della
notazione ed indicare la parent map di un place graph A con A. Il contesto consente
di prevenire ambiguita; per esempio in B ◦A stiamo parlando di place graph, mentre
in B(A(v)) stiamo parlando delle parent map. Quindi (B◦A)(v) indica la place map
del place graph (B ◦ A) applicata al nodo v. Si noti in particolare che (B ◦ A)(v) e
diverso da B(A(v)); infatti se v ∈ VA allora (B ◦ A)(v) e uguale a A(v) se questo e
un nodo, altrimenti e uguale a B(A(v)).
Definizione 4 (tensore). L’operazione di tensore ⊗ e definita nel seguente modo:
dati due place graph Ai : mi → ni(i = 0, 1) sia A0 ⊗ A1 : m0 + m1 → n0 + n1
definito quando A0 ed A1 hanno insiemi di nodi disgiunti; per la parent map prima
riordinano siti e radici di A1 aggiungendogli m0 e n0 rispettivamente, poi si uniscono
le parent map.
L’operazione di tensore e parziale in quanto e definita quando A0 ed A1 hanno
insiemi di nodi disgiunti, quindi l’insieme dei nodi risultante e V0 ] V1. Intuitiva-
mente l’operazione di tensore su due place graph non fa altro che affiancarli. Queste
operazioni danno luogo ad una precategoria monoidale con supporto; per i dettagli
(pre)categoriali, si veda [8].
2.2 Directed link graph
In questa sezione si presentano i directed link graph come definiti in [4], con le loro
proprieta.
2.2.1 Definizioni
Sia K un insieme di signature di controlli dati.
Definizione 5. Un’interfaccia polarizzata X e una coppia di insiemi disgiunti di
nomi X = (X−, X+); le due componenti sono chiamate faces verso il basso e verso
2.2. DIRECTED LINK GRAPH 9
x y z
w
e
v0 v1
A : ({w}, ∅)→ ({x, y, z}, ∅)x y z
w
e
v0v1
B : (∅, {x, y, z})→ (∅, {w})x y
zw
e
C : (∅, {x, y})→ (∅, {w, z})
Figura 2.1: Esempi di directed link graph
l’alto rispettivamente. Un directed link graph A : X → Y e A = (V,E, ctrl, link)
dove X ed Y sono l’interfaccia interna ed esterna, V e l’insieme dei nodi, E e
l’insieme degli archi, ctrl : V → K e la mappa dei controlli, e link : Pnt→ Lnk e la
mappa dei link, dove le porte, i punti e i link di A sono definiti come segue.
Prt(A) ,∑v∈V
ar(ctrl(v)) Pnt(A) , X+ ] Y − ] Prt(A) Lnk(A) , X− ] Y + ] E
La mappa dei link non puo connettere nomi verso il basso e verso l’alto della stes-
sa interfaccia, la seguente condizione deve essere rispettata: (link(X+) ∩ X−) ∪(link(Y −) ∩ Y +) = ∅.
I directed link graph sono rappresentati graficamente come grafi ordinari, con
la differenza che gli archi sono rappresentati esplicitamente come vertici del grafo,
e non come iper-archi che connettono punti e nomi; i punti e i nomi sono associati
agli archi (o altri nomi) con archi semplici diretti.
Questa notazione mira a rendere chiaro il “flusso delle richieste delle risorse”:
porte e nomi nelle interfacce possono essere associati a risorse interne o esterne. Nel
primo caso porte e nomi sono connessi ad un arco; questi nomi sono detti “verso
l’interno” perche dichiarano al contesto come ottenere risorse interne. Nel secondo
caso, le porte e i nomi sono connessi ad un nome verso l’esterno, che aspetta di
essere mappato dal contesto in una risorsa.
D’ora in poi quando si parlera di “interfaccia” o “link graph” si intendera “in-
terfaccia polarizzata” e “directed link graph” salvo dove indicato diversamente.
Definizione 6 (composizione di link graph). Dati due link graph Ai = (Vi, Ei, ctrli, linki) :
Xi → Xi+1(i = 0, 1), la composizione A1◦A0 : X0 → X2 e definita quando i due link
graph hanno archi e nodi disgiunti. In questo caso A1 ◦A0 = (V,E, ctrl, link), dove
10 CAPITOLO 2. BIGRAFI DIRETTI
V , V0]V1, ctrl , ctrl0]ctrl1, E , E0]E1 e link : X+0 ]X
−2 ]P → E]X−0 ]X
+2
e definita come segue (dove P = Prt(A0) ] Prt(A1)):
link(p) ,
link0(p) se p ∈ X+
0 ] Prt(A0) e link0(p) ∈ E0 ]X−0link1(x) se p ∈ X+
0 ] Prt(A0) e link0(p) = x ∈ X+1
link1(p) se p ∈ X−2 ] Prt(A1) e link1(p) ∈ E1 ]X+2
link0(x) se p ∈ X−2 ] Prt(A1) e link1(p) = x ∈ X−1
Il link graph identita di X e idX , (∅, ∅, ∅K, IdX−]X+) : X → X.
E facile controllare che la composizione e associativa, e che dato un link graph
A : X → Y , la composizione A ◦ idX e idY ◦ A sono definite e uguali ad A. La
definizione di link graph proibisce connessioni tra nomi della stessa interfaccia per
impedire mappe di link indefinite dopo una composizione.
Definizione 7 (Supporto). Il supporto di un link graph A = (V,E, ctrl, link) : X →Y e l’insieme |A| = V ⊕ E.
Definizione 8 (idle, lean, aperto, chiuso, peer). Sia A : X → Y un link graph.
Un link l ∈ Lnk(A) e idle se non appartiene all’immagine della mappa dei link
(l /∈ link(Pnt(A))). Il link graph A e lean se non ci sono link idle.
Un link l e aperto se e un nome interno verso il basso o un nome esterno verso
l’alto (l ∈ X− ∪ Y +); e chiuso se e un arco.
Un punto p e aperto se link(p) e un link aperto, altrimenti e chiuso. Due punti
p1, p2 sono peer se sono mappati allo stesso link, quindi link(p1) = link(p2).
Definizione 9 (Tensore tra link graph). Date due interfacce X, Y se queste so-
no disgiunte allora X ⊗ Y , (X− ] Y −, X+ ] Y +). Dati due link graph Ai =
(Vi, Ei, ctrli, linki) : Xi → Yi(i = 0, 1), se il tensore sulle interfacce e definito e gli
insiemi dei nodi e degli archi sono disgiunti il tensore A0⊗A1 : X0⊗X1 → Y0⊗ Y1
e definito come A0 ⊗A1 , (V0 ] V1, E0 ] E1, ctrl0 ] ctrl1, link0 ] link1).
2.3 Bigrafi diretti
Possiamo ora definire i bigrafi diretti come composizione di place graph e directed
link graph.
Definizione 10 (Interfaccia). Un’interfaccia e composta da un’ampiezza (finita or-
dinale) e da una coppia di insiemi finiti di nomi. Siano I = 〈m,X〉 e J =
〈n, Y 〉 due interfacce. Un bigrafo diretto G con signature K da I in J e G =
2.3. BIGRAFI DIRETTI 11
(V,E, ctrl, prnt, link) : I → J , dove I e J sono le interfacce interna ed esterna ri-
spettivamente, e prnt, ctrl, link sono le mappe dei parent, dei controlli e dei link, in
modo che GP , (V, ctrl, prnt) : m→ n sia un place graph e GL , (V,E, ctrl, link) :
X → Y sia un directed link graph.
Indicheremo G come combinazione di GP e GL con G = 〈GP , GL〉. In questa
notazione un place graph e un directed link graph possono essere messi insieme se e
solo se hanno lo stesso insieme di nodi.
Definizione 11 (Composizione di bigrafi diretti). Siano G : I → J e H : J → K
due bigrafi diretti, con gli insiemi di nodi e archi disgiunti, allora la loro composizione
e definita come composizione delle componenti:
H ◦G , 〈HP ◦GP , HL ◦GL〉 : I → K
Il bigrafo diretto identita di I = 〈m,X〉 e 〈idm, IdX−]X+〉 : I → J .
Definizione 12 (Tensore su bigrafi diretti). Siano I = 〈m,X〉 e J = 〈n, Y 〉 con X
ed Y disgiunti, allora 〈m,X〉⊗〈n, Y 〉 , 〈m+n, (X−]Y −, X+]Y +)〉. Il tensore di
due bigrafi diretti Gi : Ii → Ji e definito quando il tensore sulle interfacce e definito
e gli insiemi di nodi ed archi dei due bigrafi sono disgiunti, allora:
G0 ⊗G1 , 〈GP0 ⊗GP1 , GL0 ⊗GL1 〉 : I0 ⊗ I1 → J0 ⊗ J1
2.3.1 Algebra
Si introduce ora l’algebra dei bigrafi diretti, per maggiori informazioni si consulti
[3].
Introduciamo alcune notazioni.
Notazione
Un’interfaccia 〈0, (X−, X+)〉 si abbrevia con (X−, X+); un insieme singoletto {x}con x; e 〈m, (∅, ∅)〉 con m. Le interfacce (∅, ∅) e 0 indicano la stessa interfaccia,
l’origine ε. Qundi l’identita idε puo essere espressa con ε, (∅, ∅) oppure 0.
Un bigrafo A : (∅, X+) → (∅, Y +) e definito da una (non necessariamente su-
riettiva) funzione σ : X+ → Y +, chiamata substitution, se non ha nodi e archi e
la mappa dei link e σ; analogamente un bigrafo A : (X−, ∅) → (Y −, ∅) e definito
da una (non necessariamente suriettiva) funzione δ : Y − → X−, chiamata fusion,
12 CAPITOLO 2. BIGRAFI DIRETTI
se non ha ne nodi ne archi e la mappa dei link e δ. Abusando della notazione scri-
veremo σ e δ per indicare i rispettivi bigrafi. Siano ~x, ~y due vettori della stessa
lunghezza; scriveremo (y0/x0, y1/x1, . . . ) o M~y~x dove tutti gli xi sono distinti, per la
mappa suriettiva xi 7→ yi; similmente scriveremo (y0/x0, y1/x1, . . . ) o O~y~x dove tutti
gli yi sono distinti, par la mappa suriettiva yi 7→ xi.
Indichiamo con MX : (∅, ∅) → (∅, X) il bigrafo definito dalla substitution vuota
σ : ∅ → X, allo stesso modo indichiamo con OX : (X, ∅) → (∅, ∅) il bigrafo definito
dalla fusion vuota δ : ∅ → X.
Si noti che ogni substitution σ puo essere espressa unicamente come σ = τ ⊗MX , dove τ e una substitution suriettiva; invece ogni fusion δ puo essere espressa
unicamente come δ = ζ ⊗OX , dove ζ e una fusion suriettiva. Indiceremo le rename
con α, ovvero le substitution biiettive e le fusion biiettive.
Introduciamo quindi i bigrafi closure. La closure HNxy : (∅, y)→ (x, ∅) non ha nodi,
un unico arco e la mappa dei link e link(x) = e = link(y). Altri due tipi di closure
si ottengono combinando la closure HNxy e Mx oppure Oy rispettivamente:
• La up-closure Ny : (∅, y)→ (∅, ∅) non ha nodi, ha un arco e link(y) = e;
• La down-closure Hx : (∅, ∅)→ (x, ∅) non ha nodi, ha un arco e link(x) = e;
Definizione 13 (wiring). Un wiring e un bigrafo le cui interfacce hanno ampiezza
0 (quindi non ha nodi). I wiring ω sono generati dalla composizione e tensore di tre
elementi base: substitutions σ : (∅, X+) → (∅, Y +), fusions δ : (Y −, ∅) → (X−, ∅) e
closures HNxy : (∅, y)→ (x, ∅).
Definizione 14 (Bigrafo primo). Un’interfaccia e prima se ha ampiezza uguale
ad 1. Spesso si abbrevia un’interfaccia prima 〈1, (X−, X+)〉 con 〈(X−, X+)〉, in
particolare 〈(∅, ∅)〉 = 1. Un bigrafo primo P : 〈m, (Y −, ∅)〉 → 〈(∅, X+)〉 non ha
nomi interni verso l’alto e non ha nomi esterni verso il basso, ad ha una interfacia
esterna prima.
Un importante bigrafo primo e mergem : m→ 1, non ha nodi e mappa m siti in un’u-
nica radice. Un bigrafo G : n→ 〈m, (X−, X+)〉 senza nomi nell’interfaccia interna,
puo essere convertito in un bigrafo primo come segue: (mergem ⊗ id(X−,X+)) ◦G.
Definizione 15 (Bigrafo discreto). Un bigrafo D e discreto se non ha archi e la
sua mappa dei link e una biiezione. Questo vuol dire che tutti i punti sono aperti,
nessuna coppia di punti e peer e nessun link e idle.
2.3. BIGRAFI DIRETTI 13
Il tensore e la composizione generano bigrafi discreti se gli argomenti sono di-
screti.
Definizione 16 (ion). Per ogni controllo non atomico K con arieta k e una coppia
di sequenze ~x− e ~x+ di nomi distinti, la cui lunghezza complessiva e k, definiamo il
discrete ion K(v)~x+
~x− : 〈(~x−, ∅)〉 → 〈(∅, ~x+)〉 come il bigrafo con un unico K-nodo v,
le cui porte sono separatamente mappate su ~x− oppure su ~x+. Si omettera v quando
si potra capire.
Uno ion arbitrario (non discreto) e formato dalla composizione di ω⊗id1 con uno
discreto. Spesso si omettera . . .⊗ idI nelle composizioni quando non c’e ambiguita.
Per esempio si scrivera mergem◦G intendendo (mergem⊗id(X−,X+))◦G e K(v)~x+
~x−◦Pintendendo (K(v)~x
+
~x− ⊗ id(Y −,Y +)) ◦ P .
Ora definiamo qualche variante del tensore che permette la condivisione dei nomi.
Nei bigrafi diretti la condivisione puo riguardare i nomi dell’interfaccia interna verso
il basso o i nomi dell’interfaccia esterna verso l’alto, come descritto dalle definizioni
che seguono.
Definizione 17 (Sharing products). L’outer sharing product, inner sharing product,
e lo sharing product di due link graph Ai : Xi → Yii = (0, 1) sono definiti come segue:
(X−, X+) (Y −, Y +) , (X− ] Y −, X+ ∪ Y −)
(X−, X+) � (Y −, Y +) , (X− ∪ Y −, X+ ] Y −)
A0 A1 , (V0 ] V1, E0 ] E1, ctrl0 ] ctrl1, link0 ] link1) : X0 ⊗X1 → Y0 Y1
A0 �A1 , (V0 ] V1, E0 ] E1, ctrl0 ] ctrl1, link0 ] link1) : X0 �X1 → Y0 ⊗ Y1
A0 ‖ A1 , (V0 ] V1, E0 ] E1, ctrl0 ] ctrl1, link0 ] link1) : X0 �X1 → Y0 Y1
definiti quando le loro interfacce sono definite e Ai hanno insiemi di nodi e archi
disgiunti.
L’outer sharing product, inner sharing product, e lo sharing product di due bigrafi
Gi : Ii → Ji sono definiti estendendo le rispettive operazioni sui link graph con il
tensore sull’ampiezza dei place graph.
〈m,X〉 〈n, Y 〉 , 〈n+m,X Y 〉 〈m,X〉 � 〈n, Y 〉 , 〈n+m,X � Y 〉
14 CAPITOLO 2. BIGRAFI DIRETTI
G0 G1 , 〈GP0 ⊗GP1 , GL0 GL1 〉 : I0 ⊗ I1 → J0 J1
G0 �G1 , 〈GP0 ⊗GP1 , GL0 �GL1 〉 : I0 � I1 → J0 ⊗ J1
G0 ‖ G1 , 〈GP0 ⊗GP1 , GL0 ‖ GL1 〉 : I0 � I1 → J0 J1
definiti quando le loro interfacce sono definite e Gi hanno insiemi di nodi ed archi
disgiunti.
E facile verificare che , �, ‖ sono associative con l’unita ε. Un altro modo per
costruire gli sharing product di due bigrafi G0, G1 e di dividere i nomi di G0 e G1,
quindi fare il tensore dei bigrafi e poi riunire i nomi:
Definizione 18. Siano G0 e G1 bigrafi con nodi e archi disgiunti, allora:
G0 G1 = σ(G0 ⊗ τG1ζ) G0 G1 = (G0 ⊗ τG1ζ)δ G0 G1 = σ(G0 ⊗ τG1ζ)δ
dove le substitutions σ e τ sono definite nel seguente modo: se zi(i ∈ n) sono i nomi
dell’interfaccia esterna verso l’alto condivisi da G0 eG1, e wi sono nomi nuovi in
biiezione con i zi , allora τ(zi) = wi e σ(wi) = σ(zi) = zi(i ∈ n). Le fusions δ e ζ
sono definite similmente ma sui nomi verso il basso dell’interfaccia interna.
Definizione 19 (Prime products). Il prime outer sharing product e prime sharing
product sono definiti come segue:
〈m, (X−, X+)〉 & 〈n, (Y −, Y +)〉 , 〈(X− ] Y −, X+ ∪ Y +)〉
G0 & G1 , merge(width(J0)+width(J1)) ◦ (G0 G1) : I0 ⊗ I1 → J0 & J1
G0 | G1 , merge(width(J0)+width(J1)) ◦ (G0 ‖ G1) : I0 � I1 → J0 & J1
definiti quando le loro interfacce sono definite e Gi ha nodi ed archi disgiunti.
E facile dimostrare che & e | sono associativi, con l’unita 1 quando applicati a
bigrafi primi. Si noti che per un wiring ω e un bigrafo primo P , si ha ω & P = ωP e
ω | P = ω ‖ P , in quanto in questo caso queste operazioni hanno lo stesso significato.
Teorema 1 (Forma normale discreta). Ogni bigrafo G puo essere espresso unica-
mente come G = (ω ⊗ idn) ◦ D ◦ (ω′ ⊗ idm), dove D e un bigrafo discreto e ω, ω
′
sono due wiring che soddisfano le seguenti condizioni:
• in ω, se due nomi dell’interfaccia esterna verso il basso sono peer, allora
puntano ad un arco;
2.3. BIGRAFI DIRETTI 15
• in ω′
non ci sono archi, e nessuna coppia di nomi dell’interfaccia interna
verso l’alto sono peer, (sui nomi dell’interfaccia interna verso l’alto ω′
e una
rinomina, pero i nomi dell’interfaccia esterna verso il basso possono essere
peer).
Ogni bigrafo discreto D : 〈m, (X−, X+)〉 → 〈n, (Y −, Y +)〉 puo essere fattorizzato
in modo unico sul dominio di ogni fattore Di, come:
D = α⊗ ((D0 ⊗ . . .⊗Dn−1) ◦ (π ⊗ iddom( ~D)))
con α una rinomina, ogni Di primo e discreto, e π una permutazione.
Dimostrazione Per la prima parte, si consideri un bigrafo G : 〈n, (X−, X+)〉 →〈m, (Y −, Y +)〉. Si divide G in tre parti: un bigrafo discreto D : 〈n, (Z−, Z+)〉 →〈m, (W−,W+)〉 e due wiring ω : (W−,W+) → (Y −, Y +) e ω
′: (X−, X+) →
(Z−, Z+) che soddisfano le seguenti condizioni. Si procede per casi:
p ∈ P , linkG(p) = e ∈ E : si aggiunge un nuovo nomi we ∈ W+ e si definisce
linkD(p) = we e linkw(we) = e;
p ∈ P , linkG(p) = y ∈ Y + : si aggiunge un nuovo nomi wy ∈ W+ e si definisce
linkD(p) = wy e linkw(wy) = y;
p ∈ P , linkG(p) = x ∈ X− : questo caso e analogo al precedente;
y ∈ Y −, linkG(y) = e ∈ E : si definisce linkw(y) = e;
x ∈ X+, linkG(x) = e ∈ E : aggiungiamo un nome nuovo ze ∈ Z+, un nuovo nome
zx ∈ Z− e si definisce linkω′ (x) = ze, linkD(ze) = we, linkω(we) = e;
y ∈ Y −, linkG(y) = x ∈ X− : aggiungiamo un nome nuovo wx ∈ W−, un nuovo
nome zx ∈ Z− e si definisce linkω(y) = wx, linkD(wx) = zx, linkω′ (zx) = x;
x ∈ X+, linkg(x) = y ∈ Y + : questo caso e analogo al precedente, e sufficiente
invertire la direzione dei link e scambiare la regola di ω con ω′.
Si noti che non ci sono nomi idle in Z−, Z+, W− e W+, quindi questi insiemi sono
formati solo da nomi nuovi definiti durante la dimostrazione. Inoltre le tre condizioni
sopra sono valide in quando creiamo un nuovo nome ogni volta che serve.
La dimostrazione della seconda parte e facile. Siccome l’interfaccia esterna di
D ha ampiezza n, possiamo decomporre D in n parti discrete e prime, ottenendo
16 CAPITOLO 2. BIGRAFI DIRETTI
D0 ⊗ . . . ⊗Dn−1. La rinomina α descrive le connessioni tra l’interfaccia interna ed
esterna. Quindi la permutazione π da la giusta sequenza dei siti, in modo da poter
eseguire il tensore di Di (i = 0, . . . , n − 1) in qualsiasi ordine. Chiameremo questa
fattorizzazione forma normale discreta (DNF).
2.3.2 Bigrafi elementari
Si introducono ora i bigrafi elementari, in grado di definire qualsiasi bigrafo in forma
normale discreta.
y
x
HNxy : (∅, y)→ (x, ∅)
closure
y
x1 x2 . . . xn
. . .
MyX : (∅, X)→ (∅, y)
substitution
x
y1 y2 . . . ym
. . .
OYx : (x, ∅)→ (Y, ∅)
fusion
Figura 2.2: Bigrafi che formano i wiring
I primi tre bigrafi, costruiscono tutti i wiring, ovvero tutti i link graph che non
hanno nodi. Tutte le substitutions (fusions risp.) possono essere ottenute con tensori
di substitutions elementari MyX (fusions OY
x risp.); dal tensore si una substitution sin-
goletto Myx e/o fusion singoletto Ox
y si ottengono tutte le rename. Dalla composizione
e il tensore di substitutions, fusions e closures si ottengono tutti i wiring.
1 : ε→ 1barren root
0 1
merge : 2→ 1fonde 2 siti in 1 radice
1 0
γ1,1 : 2→ 2scambia 2 siti
Figura 2.3: Bigrafi che formano tutti i place graph senza nodi
I tre bigrafi della figura 2.3 definiscono tutti i place graph senza nodi; per esempio
mergem : m→ 1, che fonde m siti in un’unica radice, e definito come:
merge0 , 1 mergem+1 , merge ◦ (id1 ⊗megem)
Si noti che merge1 = id e merge2 = merge, e che tutte le permutazioni π : m→ m
sono costruite con composizioni e tensori di γm,n. L’ultimo tipo di bigrafo che serve
2.3. BIGRAFI DIRETTI 17
x−1 x−2. . . x−j . . . x−k−1x
−k
x+1 x
+2. . . x+
j. . . x+
n−1x+n
. . .
. . .
. . .
. . .
K(l) : 〈(~x−, ~Y +)〉 → 〈(~Y −, ~x+)〉un discrete ion
Figura 2.4: Un discrete ion
per esprimere un qualsiasi bigrafo e il discrete ion K~x+
~x− (fig. 2.4).
Ogni bigrafo puo essere espresso in forma normale discreta quindi ogni bigrafo
puo essere espresso come composizione e tensore di questi bigrafi elementari.
3Implementazione dei bigrafi diretti
In questo capitolo sara presentata l’implementazione di signatures e bigrafi descri-
vendo la struttura dati e le operazioni piu importanti, successivamente saranno
descritti i bigrafi elementari, la forma normale e l’uguaglianza tra bigrafi.
D’ora in poi quando si parlera di “interfaccia”, “link graph” e “bigrafo” si inten-
dera “interfaccia polarizzata”, “directed link graph” e “bigrafo diretto” salvo dove
indicato diversamente.
3.1 Linguaggio utilizzato
Il linguaggio scelto per l’implementazione e l’Objective Caml (OCaml)1, un’esten-
sione ad oggetti del linguaggio Caml che e a sua volta un dialetto delle famiglia di
linguaggi di programmazione ML.
OCaml e un progetto open source gestito e sviluppato principalmente dall’INRIA
ovvero l’istituto nazionale per la ricerca nell’informatica e nell’automazione che fa
parte dell’istituto nazionale francese per la ricerca. Esso e stato sviluppato per essere
veloce, efficiente e richiedere poca memoria.
Essendo open source inoltre fa si che sia disponibile sulle piu comuni piattaforme
software e hardware.
Le caratteristiche che interessavano per la scrittura del codice sono:
Funzionale Le funzioni possono essere annidate e passate come parametri.
Inferenza dei tipi I tipi dei valori sono automaticamente inferiti non devono essere
specificati.
Polimorfismo Nei casi dove diversi tipi possono essere validi, ognuno puo essere
usato.1Si veda: http://caml.inria.fr/
20 CAPITOLO 3. IMPLEMENTAZIONE DEI BIGRAFI DIRETTI
Pattern Matching Valori, ed in particolare i contenuti di strutture dati, possono
essere confrontati con pattern per determinare l’azione adeguata.
Oggetti C’e la possibilita di definire oggetti.
Garbage collector I valori che non vengono usati sono automaticamente deallo-
cati.
3.2 Struttura dati
3.2.1 Insieme delle signature
Una struttura ausiliaria quella del bigrafo e l’insieme delle signature. La struttura
dati utilizzata per implementarla e quella del modulo Set che fa parte della libreria
standard di OCaml, questa struttura dati implementa la gestione di un insieme su
un tipo ordinato.
E stato creato un tipo Signature per descrivere gli elementi dell’insieme, il tipo e
formato da elementi (signature, (attivita, arieta)) dove signature e di tipo stringa,
attivita e di tipo stringa ed arieta e un intero. Successivamente e stata definita la
funzione compare su questo tipo per definire un ordine, questa confronta solo il primo
elemento della coppia in modo da non permettere che ci siano due signature uguali
con arieta o attivita diversa.
module S ignature =s t r u c t
type t = s t r i n g ∗ ( s t r i n g ∗ i n t )l et compare ( s ig1 , ( t1 , n1 ) ) ( s ig2 , ( t2 , n2 ) ) =
i f s i g 1 < s i g 2 then − 1else i f s i g 1 > s i g 2 then 1else 0
end ; ;
La definizione del modulo che implementa l’insieme e :
module S ignatureSet = Set . Make( S ignature ) ; ;
Per rendere piu usabile questa struttura dati e stata creata una classe con dei
metodi per l’inserimento, rimozione e i metodi per ottenere l’arieta o l’attivita a
partire dalla signature.
3.2.2 Classe directedbg
La struttura dati che rappresenta una bigrafo e stata implementante come una classe.
La classe ha in seguenti attributi:
3.2. STRUTTURA DATI 21
v Insieme dei nodi del bigrafo.
e Insieme degli archi del bigrafo.
prnt Lista che descrive il place graph.
link Lista dei link del bigrafo.
ctrl Lista dei controlli.
innerint Interfaccia interna.
outerint Interfaccia esterna.
Per rappresentare gli elementi degli insiemi e stato costruito un tipo formato
da una coppia di stringhe (elemento,tipo) dove elemento e la stringa che identifi-
ca un nome, nodo, arco etc. . . mentre tipo e una stringa che identifica la classe
dell’elemento, puo essere uno dei seguenti:
“v” Rappresenta un nodo.
“e” Rappresenta un arco.
“inm” Rappresenta un nome dell’interfaccia interna verso il basso.
“inp” Rappresenta un nome dell’interfaccia interna verso l’alto.
“onm” Rappresenta un nome dell’interfaccia esterna verso il basso.
“onp” Rappresenta un nome dell’interfaccia esterna verso l’alto.
La definizione del tipo e la seguente:
module S t r i ng E l t =s t r u c t
type t = s t r i n g ∗ s t r i n gl et compare ( e l t 1 , t1 ) ( e l t 2 , t2 ) =
i f e l t 1 < e l t 2 then − 1else i f e l t 1 > e l t 2 then 1else 0
end ; ;
Si noti che il tipo non conta ai fini dell’ordinamento. Segue la definizione del modulo:
module S t r ingSe t = Set . Make( S t r i n gE l t ) ; ;
Seguono i dettagli sull’implementazione delle singole componenti.
22 CAPITOLO 3. IMPLEMENTAZIONE DEI BIGRAFI DIRETTI
3.2.2.1 Insieme dei nodi
L’insieme dei nodi e un attributo della classe di tipo StringSet.
La gestione dell’inserimento dei nodi richiede due operazioni sulla struttura dati,
l’inserimento del nodo con il tipo “v” nell’insieme e l’aggiunta alla lista ctrl del
nome del nodo e della sua arieta. Per permettere questo al metodo che effettua
l’inserimento del nodo va passato anche la sua signature e la classe delle signature.
method addnode : s t r i n g −> s t r i n g −> Bigraph . s i g n a t u r e s −>uni t
E presente un’altro metodo per l’inserimento dei nodi che non richiede la classe delle
signature ma viene passata direttamente la sua arieta.
method addnodear : s t r i n g −> i n t −> uni t
3.2.2.2 Insieme degli archi
L’inserimento degli archi richiede solo l’aggiunta dell’elemento all’insieme e. Il tipo
“e” viene aggiunto automaticamente.
method addedge : s t r i n g −> uni t
3.2.2.3 Place graph
Gli elementi del place graph sono radici, nodi e siti. Si e scelto di rappresentare siti
e radici tramite interi mentre i nodi con stringhe.
L’intera struttura del place graph e stata implementata con una lista di elementi
del tipo (figlio,padre). L’inserimento di una coppia si esegue semplicemente aggiun-
gendola alla fine della lista. Per realizzare questo si e sfruttato il polimorfismo sui
dati che permette OCaml come si nota dalla definizione del tipo della lista:
type t p l a c e = SiteNode of i n t ∗ s t r i n g| SiteRoot of i n t ∗ i n t| NodeRoot of s t r i n g ∗ i n t| NodeNode of s t r i n g ∗ s t r i n g ; ;
Il tipo della lista e tplace e le possibili combinazioni sono:
- Sito figlio di un nodo.
- Sito figlio di una radice.
- Nodo figlio di una radice.
- Nodo figlio di un nodo.
3.2. STRUTTURA DATI 23
Visto i tipi diversi dei dati, per ogni diversa combinazione e presente un metodo:
method addprnts i tenode : i n t −> s t r i n g −> uni tmethod a ddprn t s i t e r oo t : i n t −> i n t −> uni tmethod addprntnoderoot : s t r i n g −> i n t −> uni tmethod addprntnodenode : s t r i n g −> s t r i n g −> uni t
Quando si chiamano i metodi viene controllato che i nodi esistano e il numero che
identifica la radice sia minore del numero totale di radici.
3.2.2.4 Link graph
La struttura del link graph e stata resa tramite una lista che contiene delle coppie
del tipo (sorgente,destinazione).
Un link puo partire da due tipi di elementi: una porta oppure un nome. La porta
e rappresentata tramite un intero mente i nomi sono rappresentati con stringhe. Sono
stati creati quindi due diversi tipi per la sorgente :
type tpo in t = Port of ( i n t ∗ s t r i n g )| Name of s t r i n g ∗ s t r i n g ; ;
Il tipo Port per rappresentare la porta usa una coppia (numero porta, nodo) mentre
il tipo Name e formato dalla coppia (nome, tipo) dove il tipo indica l’interfaccia a
cui appartiene.
La destinazione di un link puo essere un arco oppure un nome, entrambi sono
rappresentati da stringhe quindi il tipo e comune:
type t l i n k = NameEdge of s t r i n g ∗ s t r i n g ; ;
Il primo elemento della coppia rappresenta la destinazione mentre la seconda parte
rappresenta il tipo “e”’ se e un arco, il tipo associato all’interfaccia a cui appartiene
se e un nome.
Visto che ci sono due tipi per la sorgente del link vi sono due metodi per inserirli,
uno per i link che partono da una porta e uno per i link che partono da nomi:
method addl inkfromport : i n t −> s t r i n g −> s t r i n g −>s t r i n g −> uni t
method addlinkfromname : s t r i n g −> s t r i n g −> s t r i n g −>s t r i n g −> uni t
Quando si inseriscono i link viene controllato che la sorgente e la destinazione
esistano e che sia un link tra quelli permessi, essi sono:
- Da una porta ad un arco.
- Da una porta ad un nome verso il basso dell’interfaccia interna.
24 CAPITOLO 3. IMPLEMENTAZIONE DEI BIGRAFI DIRETTI
- Da una porta ad un nome verso l’alto dell’interfaccia esterna.
- Da un nome dell’interfaccia interna verso l’alto ad un arco.
- Da un nome dell’interfaccia interna verso l’alto ad un nome dell’interfaccia
esterna verso l’alto.
- Da un nome dell’interfaccia esterna verso il basso ad un arco.
- Da un nome dell’interfaccia esterna verso il basso ad un nome dell’interfaccia
interna verso il basso.
Controllando il tipo passato insieme agli elementi e possibile controllare che il link
sia fra quelli elencati.
3.2.2.5 Lista dei controlli
La lista dei controlli e implementata come una lista di elementi del tipo (nodo,arieta)
dove nodo e la stringa che identifica il nodo e arieta e un intero che rappresenta la
sua arieta. Vi sono due metodi per l’inserimento:
method addc t r l : s t r i n g −> s t r i n g −> Bigraph . s i g n a t u r e s −> uni tmethod a d d c t r l a r : s t r i n g −> i n t −> uni t
tutti e due necessitano del nome del nodo ma il primo prende in ingresso la sua
signature e la classe delle signature, mentre il secondo accetta direttamente l’arieta
del nodo.
Questi sono i metodi usati dalle funzioni che aggiungono i nodi al bigrafo.
3.2.2.6 Interfaccia interna
La struttura scelta per rappresentare l’interfaccia e la coppia (numero siti, (nomi
verso il basso, nomi verso l’alto)) dove il primo elemento e un intero e il secondo e
una coppia di insiemi.
Risulta quindi cosı definita:
va l mutable i n n e r i n t : i n t ∗ ( S t r ingSe t . t ∗ St r ingSe t . t )
Sono presenti i metodi per definire il numero di siti ed aggiungere nomi nei due
insiemi dell’interfaccia:
method s e t i n n e r i n t n : i n t −> uni tmethod addxminus : s t r i n g −> uni tmethod addxplus : s t r i n g −> uni t
3.3. OPERAZIONI DI BASE 25
I metodi addxplus e addxminus aggiungono i nomi all’insieme dei nomi verso il basso
ed alto rispettivamente. Se il nome e verso il basso verra aggiunto il tipo “inm”,
“inp” per quelli verso l’alto.
3.2.2.7 Interfaccia esterna
L’interfaccia esterna e strutturata analogamente a quella interna, con la differenza
che l’intero rappresenta il numero di radici del bigrafo e non il numero di siti. I
metodi per impostare il numero di radici ed aggiungere i nomi sono:
method s e t o u t e r i n t n : i n t −> uni tmethod addyminus : s t r i n g −> uni tmethod addyplus : s t r i n g −> uni t
I tipi aggiunti per i nomi in questo caso sono “onm” per i nomi verso il basso, “onp”
per i nomi verso l’alto.
3.3 Operazioni di base
3.3.1 Tensore
L’operazione tensore (⊗) su due bigrafi puo essere eseguita solo se le loro compo-
nenti sono disgiunte, per controllare che questa condizione sussista e stato creato il
metodo aredisjoint che richiede un bigrafo come parametro. Questo metodo chiama
diversi metodi che controllano se le componenti rappresentate internamente e quelle
del bigrafo passato sono disgiunte, il risultato e di tipo booleano: true se tutte le
componenti sono disgiunte mentre false se non lo sono.
Se le componenti sono disgiunte si puo procedere con l’operazione di tensore, vie-
ne creato un nuovo bigrafo le cui componenti sono l’unione di quelle dei due bigrafi
su cui si esegue il tensore. Per le componenti che sono rappresentate tramite insiemi
(nodi, archi . . . ) il modulo StringSet dispone dell’operazione di unione insiemistica,
mentre per le componenti che sono rappresentate come liste si opera semplicemente
concatenandole. La lista prnt del bigrafo passato come parametro va pero modifi-
cata. Visto che le radici partono da 0 e necessario incrementarle con il numero di
radici del bigrafo rappresentato internamente. Per i siti invece prima si controlla
se i due bigrafi hanno siti in comune; se ci sono siti comuni tutti i siti del secondo
bigrafo sono incrementati del massimo sito del primo bigrafo piu uno, se no allora
non e necessario fare modifiche. In questo modo si evitano sovrapposizioni.
Quando si esegue il tensore il numero di radici del bigrafo che si genera e dato
dalla somma del numero delle radici dei due bigrafi, lo stesso vale per i siti.
26 CAPITOLO 3. IMPLEMENTAZIONE DEI BIGRAFI DIRETTI
Segue il codice del metodo:
method tenso r ( dbg : d i r e c t edbg ) =i f s e l f#a r e d i s j o i n t dbg thennew d i r e c t edbg ( S t r ingSe t . union dbg#getnodes v )
( S t r ingSe t . union dbg#getedges e )( ( s e l f#addn dbg#getprnt ) @ prnt )( dbg#g e t l i n k @link )( dbg#g e t c t r l @ c t r l )( dbg#g e t i n n e r i n t n + s e l f#ge t inne r in tn ,( S t r ingSe t . union dbg#getxminus s e l f#getxminus ,S t r i ngSe t . union dbg#getxp lus s e l f#getxp lus ) )( dbg#g e t o u t e r i n t n + s e l f#getoute r in tn ,( S t r ingSe t . union dbg#getyminus s e l f#getyminus ,S t r i ngSe t . union dbg#getyp lus s e l f#getyp lus ) )
elser a i s e B i g r a p h s a r e n o t d i s j o i n t
I metodi il cui nome comincia con “get” servono ad ottenere le varie componenti
del bigrafo.
Nel caso che i bigrafi non siano disgiunti viene sollevata un’eccezione del tipo
Bigraphs are not disjoint .
3.3.2 Composizione
Per poter eseguire l’operazione di composizione tra due bigrafi devono sussistere due
condizioni. La prima condizione richiede che gli insiemi dei loro nodi ed archi devono
essere disgiunti. La seconda richiede che le due interfacce che formano l’interfaccia
comune siano uguali. Nel caso A1 ◦A0 , per esempio, l’interfaccia interna di A1 deve
essere uguale all’interfaccia esterna di A0.
Vengono usati i metodi aredisjointv ed aredisjointe per controllare che gli in-
siemi dei nodi ed archi rispettivamente siano disgiunti, mentre sameinterface per
controllare che i bigrafi abbiano abbiano le due interfacce su cui opera la com-
posizione uguali. Se queste condizioni non sussistono viene sollevata l’eccezione
Bigraphs have no common interface.
Sia A1 ◦ A0 la composizione da eseguire, essa crea un nuovo bigrafo e le sue
componenti verranno costruite nel seguente modo :
3.3.2.1 Insieme dei nodi
L’insieme dei nodi e semplicemente l’unione dei due insiemi dei nodi di A1 ed A0
3.3. OPERAZIONI DI BASE 27
3.3.2.2 Insieme degli archi
Anche l’insieme degli archi e ottenuto con l’unione dei due insiemi degli archi di
A1 ed A0
3.3.2.3 Place graph
La composizione sul place graph opera mettendo le radici di A0 nei siti di A1. In
pratica nel nuovo bigrafo i figli di una radice di A0 avranno come padre il padre del
rispettivo sito di A1.
Vista l’implementazione tramite liste si puo dividere la composizione sul place
graph in due parti: trovare gli elementi “interni” , ovvero che non verranno modi-
ficati dalla composizione, di A1 e per ogni elemento di A0 che ha come padre una
radice creare un nuovo elemento che ha come padre il padre del rispettivo sito di
A1, lasciando invariati gli altri. Sono stati creati due metodi che svolgono queste
operazioni compositeprntint per trovare gli elementi “interni” di A1 ed compositeprnt
per l’unione di siti con radici. La lista prnt del nuovo bigrafo e la concatenazione
del risultato di questi due metodi.
3.3.2.4 Link graph
La composizione sul link graph cerca link che partono da un bigrafo e finiscono nel-
l’interfaccia comune, per ognuno di questi si cerca nell’altro bigrafo un link che parte
dallo stesso elemento dell’interfaccia comune. Se quest’elemento esiste si crea un link
che ha come sorgente la sorgente del primo e come destinazione la destinazione del
secondo.
Il lavoro della composizione e stato diviso in due fasi: una fase consiste nel cer-
care e mantenere i link “interni” ad ognuno dei bigrafi. I link interni sono quelli
che non hanno come destinazione o sorgente l’interfaccia condivisa e che quindi non
verranno modificati dalla composizione. La seconda fase consiste nel cercare i link
che attraversano l’interfaccia condivisa, ovvero quelli che partendo da un bigrafo ar-
rivano ad un nome dell’interfaccia condivisa. Per ognuno di questi si cerca nell’altro
bigrafo un link che parte da questo nome, se esiste si crea il link composto.
Ognuna delle fasi e stata implementata con un metodo: compositelinkint per
trovare i link interni e compositelink per la composizione dei link. La lista dei link
del nuovo bigrafo sara la concatenazione dei risultati dei due metodi per ognuno dei
due bigrafi.
28 CAPITOLO 3. IMPLEMENTAZIONE DEI BIGRAFI DIRETTI
3.3.2.5 Lista dei controlli
Siccome il bigrafo risultante dalla composizione contiene i nodi di A1 ed A0 la lista
ctrl risultante sara la concatenazione delle liste dei due bigrafi.
3.3.2.6 Interfaccia interna
L’interfaccia interna del nuovo bigrafo sara uguale all’interfaccia interna di A0.
3.3.2.7 interfaccia esterna
L’interfaccia esterna del nuovo bigrafo sara uguale all’interfaccia esterna di A1.
3.3.2.8 Il metodo composition
Il metodo che esegue la composizione e composition , supponendo di disporre di due
istanze a1 ed a0 che rappresentano bigrafi la composizione A1 ◦ A0 viene eseguita
con a1#composition a0. Segue il codice del metodo:
method compos it ion ( dbg : d i r e c t edbg ) =i f ( s e l f#same in t e r f a c e dbg )
&(dbg#a r e d i s j o i n t v v )&(dbg#a r e d i s j o i n t e e )
thennew d i r e c t edbg ( S t r ingSe t . union dbg#getnodes v )
( S t r ingSe t . union dbg#getedges e )( ( s e l f#compositeprnt dbg#getprnt prnt )@( s e l f#compos i t eprnt int prnt ) )( ( s e l f#c o m p o s i t e l i n k i n t dbg#g e t l i n k ”inm” ” inp ” )@( s e l f#compos i t e l ink dbg#g e t l i n k l i n k ”onp” ” inp ” )@( s e l f#c o m p o s i t e l i n k i n t l i n k ”onp” ”onm” )@( s e l f#compos i t e l ink l i n k dbg#g e t l i n k ”inm” ”onm” ) )( dbg#g e t c t r l @ c t r l )( dbg#get inne r in tn , ( dbg#getxminus , dbg#getxp lus ) )( s e l f#getoute r in tn , ( s e l f#getyminus , s e l f#getyp lus ) )
elser a i s e Bigraphs have no common inter face
3.4 Operazioni derivate
Si procede a descrivere ora l’implementazione delle operazioni derivate ovvero sha-
ring products e prime products.
3.4. OPERAZIONI DERIVATE 29
3.4.1 Sharing products
Gli sharing products sono delle varianti del tensore che permettono la condivisione
dei nomi. La loro implementazione e molto simile a quella del tensore con la diffe-
renza che per gli insiemi dei nomi che andranno condivisi si esegue l’unione senza
controllare che essi siano disgiunti.
3.4.1.1 Outer sharing product
L’outer sharing product consente la condivisione dei nomi dell’interfaccia esterna
verso l’alto, non serve controllare che questi ultimi siano disgiunti nei due bigrafi
quindi. Il codice del metodo e il seguente:
method osp ( dbg : d i r e c t edbg ) =i f ( dbg#a r e d i s j o i n t v v )
& ( dbg#a r e d i s j o i n t e e )& ( dbg#ared i s j o i n txminus s e l f#getxminus )& ( dbg#a r e d i s j o i n t x p l u s s e l f#getxp lus )& ( dbg#ared i s j o i n tyminus s e l f#getyminus )
thennew d i r e c t edbg ( S t r ingSe t . union dbg#getnodes v )
( S t r ingSe t . union dbg#getedges e )( ( s e l f#addn dbg#getprnt ) @ prnt )( dbg#g e t l i n k @link )( dbg#g e t c t r l @ c t r l )( dbg#g e t i n n e r i n t n + s e l f#ge t inne r in tn ,( S t r ingSe t . union dbg#getxminus s e l f#getxminus ,
S t r i ngSe t . union dbg#getxp lus s e l f#getxp lus ) )( dbg#g e t o u t e r i n t n + s e l f#getoute r in tn ,( S t r ingSe t . union dbg#getyminus s e l f#getyminus ,
S t r i ngSe t . union dbg#getyp lus s e l f#getyp lus ) )else
r a i s e B i g r a p h s a r e n o t d i s j o i n t
3.4.1.2 Inner sharing product
L’inner sharing product consente la condivisione dei nomi dell’interfaccia interna
verso il basso, si omette il controllo su questi insiemi di nomi quindi. Il codice del
metodo e il seguente:
method i s p ( dbg : d i r e c t edbg ) =i f ( dbg#a r e d i s j o i n t v v )
& ( dbg#a r e d i s j o i n t e e )& ( dbg#a r e d i s j o i n t x p l u s s e l f#getxp lus )& ( dbg#ared i s j o i n tyminus s e l f#getyminus )& ( dbg#a r e d i s j o i n t y p l u s s e l f#getyp lus )
thennew d i r e c t edbg ( S t r ingSe t . union dbg#getnodes v )
30 CAPITOLO 3. IMPLEMENTAZIONE DEI BIGRAFI DIRETTI
( S t r ingSe t . union dbg#getedges e )( ( s e l f#addn dbg#getprnt ) @ prnt )( dbg#g e t l i n k @link )( dbg#g e t c t r l @ c t r l )( dbg#g e t i n n e r i n t n + s e l f#ge t inne r in tn ,( S t r ingSe t . union dbg#getxminus s e l f#getxminus ,
S t r i ngSe t . union dbg#getxp lus s e l f#getxp lus ) )( dbg#g e t o u t e r i n t n + s e l f#getoute r in tn ,( S t r ingSe t . union dbg#getyminus s e l f#getyminus ,S t r i ngSe t . union dbg#getyp lus s e l f#getyp lus ) )
elser a i s e B i g r a p h s a r e n o t d i s j o i n t
3.4.1.3 Sharing product
Lo sharing product consente la condivisione dei nomi dell’interfaccia interna verso il
basso e dei nomi dell’interfaccia esterna verso l’alto. I controlli da omettere quindi
sono gli stessi dell’outer sharing product e dell’inner sharing product. Il codice del
metodo e il seguente:
method sp ( dbg : d i r e c t edbg ) =i f ( dbg#a r e d i s j o i n t v v )
& ( dbg#a r e d i s j o i n t e e )& ( dbg#a r e d i s j o i n t x p l u s s e l f#getxp lus )& ( dbg#ared i s j o i n tyminus s e l f#getyminus )
thennew d i r e c t edbg ( S t r ingSe t . union dbg#getnodes v )
( S t r ingSe t . union dbg#getedges e )( ( s e l f#addn dbg#getprnt ) @ prnt )( dbg#g e t l i n k @link )( dbg#g e t c t r l @ c t r l )( dbg#g e t i n n e r i n t n + s e l f#ge t inne r in tn ,( S t r ingSe t . union dbg#getxminus s e l f#getxminus ,
S t r i ngSe t . union dbg#getxp lus s e l f#getxp lus ) )( dbg#g e t o u t e r i n t n + s e l f#getoute r in tn ,( S t r ingSe t . union dbg#getyminus s e l f#getyminus ,
S t r i ngSe t . union dbg#getyp lus s e l f#getyp lus ) )else
r a i s e B i g r a p h s a r e n o t d i s j o i n t
3.4.2 Prime sharing products
Le due operazioni in questo gruppo si comportano come l’outer sharing product e lo
sharing product con la differenza che il bigrafo risultante avra solo una radice. Tutti
le componenti del place graph sono resi figli di questa unica radice. Per fare questo le
liste prnt dei due bigrafi vanno analizzate e tutti gli elementi figli di una radice vanno
3.4. OPERAZIONI DERIVATE 31
resi figli della radice 0. Il numero dei siti del bigrafo passato come parametro invece
va modificato come nel tensore. il metodo che svolge quest’operazione e oneroot.
3.4.2.1 Prime outer sharing product
Operazione analoga all’outer sharing product ma tutti gli elementi del place graph
vengono resi figli della radice 0.
Segue il codice del metodo:
method posp ( dbg : d i r e c t edbg ) =i f ( dbg#a r e d i s j o i n t v v )
& ( dbg#a r e d i s j o i n t e e )& ( dbg#ared i s j o i n txminus s e l f#getxminus )& ( dbg#a r e d i s j o i n t x p l u s s e l f#getxp lus )& ( dbg#ared i s j o i n tyminus s e l f#getyminus )
thennew d i r e c t edbg ( S t r ingSe t . union dbg#getnodes v )
( S t r ingSe t . union dbg#getedges e )( ( s e l f#oneroot dbg#getprnt ) @ ( s e l f#oneroot1 prnt 0 ) )( dbg#g e t l i n k @link )( dbg#g e t c t r l @ c t r l )( dbg#g e t i n n e r i n t n + s e l f#ge t inne r in tn ,( S t r ingSe t . union dbg#getxminus s e l f#getxminus ,S t r i ngSe t . union dbg#getxp lus s e l f#getxp lus ) )(1 ,( S t r ingSe t . union dbg#getyminus s e l f#getyminus ,
S t r i ngSe t . union dbg#getyp lus s e l f#getyp lus ) )else
r a i s e B i g r a p h s a r e n o t d i s j o i n t
3.4.2.2 Prime sharing product
Operazione analoga allo sharing product ma tutti gli elementi del place graph
vengono resi figli della radice 0.
Segue il codice del metodo:
method psp ( dbg : d i r e c t edbg ) =i f ( dbg#a r e d i s j o i n t v v )
& ( dbg#a r e d i s j o i n t e e )& ( dbg#a r e d i s j o i n t x p l u s s e l f#getxp lus )& ( dbg#ared i s j o i n tyminus s e l f#getyminus )
thennew d i r e c t edbg ( S t r ingSe t . union dbg#getnodes v )
( S t r ingSe t . union dbg#getedges e )( ( s e l f#oneroot dbg#getprnt ) @ ( s e l f#oneroot1 prnt 0 ) )( dbg#g e t l i n k @link )( dbg#g e t c t r l @ c t r l )( dbg#g e t i n n e r i n t n + s e l f#ge t inne r in tn ,( S t r ingSe t . union dbg#getxminus s e l f#getxminus ,
32 CAPITOLO 3. IMPLEMENTAZIONE DEI BIGRAFI DIRETTI
St r ingSe t . union dbg#getxp lus s e l f#getxp lus ) )(1 ,( S t r ingSe t . union dbg#getyminus s e l f#getyminus ,
S t r i ngSe t . union dbg#getyp lus s e l f#getyp lus ) )else
r a i s e B i g r a p h s a r e n o t d i s j o i n t
3.5 Algebra
3.5.1 Bigrafi elementari
I bigrafi elementari possono essere creati con delle funzioni passando i parametri
necessari.
3.5.1.1 Swap
La funzione che crea il bigrafo swap prende in ingresso due parametri: m ed n. Il
bigrafo risultante ha m + n radici ed m + n siti. I primi m siti pero sono mappati
nelle ultime m radici mentre i seguenti n sono mappati nelle prime n radici.
La costruzione della lista prnt del bigrafo e eseguita dalla funzione swapaux:
l et rec swapaux m n i = i f ( i + 1) = (m + n) thenSiteRoot ( i , i − m) : : [ ]
else i f i < m thenSiteRoot ( i , i + n ) : : ( swapaux m n ( i + 1) )
elseSiteRoot ( i , i − m) : : ( swapaux m n ( i + 1 ) ) ; ;
Il codice della funzione che crea il bigrafo swap e il seguente:
l et swap m n = i f (m + n) = 0 thennew d i r e c t edbg St r ingSe t . empty St r ingSe t . empty [ ] [ ] [ ]
( 0 , ( S t r i ngSe t . empty , S t r ingSe t . empty ) )(0 , ( S t r i ngSe t . empty , S t r ingSe t . empty ) )
elsenew d i r e c t edbg St r ingSe t . empty St r ingSe t . empty( swapaux m n 0) [ ] [ ](m + n , ( S t r ingSe t . empty , S t r ingSe t . empty ) )
(m + n , ( S t r ingSe t . empty , S t r ingSe t . empty ) ) ; ;
Si nota che se m+ n = 0 la funzione ritorna un bigrafo vuoto.
3.5.1.2 Identita
La funzione che crea il bigrafo identita e la funzione id. Essa prende in ingresso il
numero di siti/radici da creare e le liste dei nomi dell’interfaccia inferiore verso l’alto
3.5. ALGEBRA 33
e quelli dell’interfaccia superiore verso l’alto. Sia n il numero di siti/radici e siano ~x
e ~y i nomi delle interfacce, interna ed esterna rispettivamente.
Viene usata la funzione swap per creare un bigrafo con n radici ognuna con un
sito come figlio. Usando 0 come valore del parametro m i siti non vengono scambiati
quindi il sito 0 avra come padre la radice 0 e cosı via. A questo bigrafo vengono
aggiunti i nomi alle interfacce e creati i link ~x → ~y tramite la funzione ausiliaria
idaux.
Il codice della funzione e:
l et id n lm lp = l et idbg = swap 0 n inidbg#addxminus l i s t lm ;idbg#addyminus l i s t lm ;idbg#a d d x p l u s l i s t lp ;idbg#a d d y p l u s l i s t lp ;idaux lm ”onm” ”inm” idbg ;idaux lp ” inp ” ”onp” idbg ;idbg ; ;
3.5.1.3 Closure
Il bigrafo closure e formato da due nomi ed un arco. Un nome appartiene all’inter-
faccia esterna verso il basso, mentre l’altro all’interfaccia interna verso l’alto, siano
essi y ed x rispettivamente. Sia e l’arco, vengono quindi creati i link x→ e ed y → e.
La funzione sfrutta la funzione id per creare un bigrafo vuoto, ovvero senza radici
e senza nomi.
Il codice della funzione e:
l et c l o s u r e x y e = l et c l = id 0 [ ] [ ] inc l#addedge e ;c l#addyminus y ;c l#addxplus x ;c l#addlinkfromname y ”onm” e ”e” ;c l#addlinkfromname x ” inp ” e ”e” ;c l ; ;
3.5.1.4 Up-closure
Variante del bigrafo closure con un’unico nome che appartiene all’interfaccia esterna
verso il basso. Sia esso y e sia e l’unico arco del bigrafo, viene creato il link y → e.
Il codice e il seguente:
l et upc lo sure y e = l et upcl = id 0 [ ] [ ] inupcl#addedge e ;upcl#addyminus y ;
34 CAPITOLO 3. IMPLEMENTAZIONE DEI BIGRAFI DIRETTI
upcl#addlinkfromname y ”onm” e ”e” ;upcl ; ;
3.5.1.5 Down-closure
Variante del bigrafo closure con un’unico nome che appartiene all’interfaccia interna
verso l’alto. Sia esso x e sia e l’unico arco del bigrafo, viene creato il link x→ e.
Il codice e il seguente:
l et downclosure x e = l et dwcl = id 0 [ ] [ ] indwcl#addedge e ;dwcl#addxplus x ;dwcl#addlinkfromname x ” inp ” e ”e” ;dwcl ; ;
3.5.1.6 Merge
Il bigrafo merge e formato da una radice con n siti al suo interno. Esso e creato dalla
funzione merge ed usa una funzione ausiliaria mergeaux che si occupa di aggiungere
n siti al bigrafo passato come parametro.
La funzione merge richiede come parametro il numero di siti da creare. Il suo
codice e il seguente:
l et merge n = i f n = 0 thennew d i r e c t edbg St r ingSe t . empty St r ingSe t . empty [ ] [ ] [ ]
( 0 , ( S t r i ngSe t . empty , S t r ingSe t . empty ) )(1 , ( S t r i ngSe t . empty , S t r ingSe t . empty ) )
elsemergeaux n (new d i r e c t edbg St r ingSe t . empty
St r ingSe t . empty [ ] [ ] [ ](n , ( S t r ingSe t . empty , S t r ingSe t . empty ) )(1 , ( S t r i ngSe t . empty , S t r ingSe t . empty ) ) ) ; ;
Si nota che se il parametro e 0 la funzione merge ritorna un bigrafo con una radice
ma nessun sito.
3.5.1.7 Barren root
Il bigrafo barren root e un bigrafo composto da una radice. La funzione che lo crea
e oneDB. Viene creato usando la funzione merge con 0 come parametro. Il codice e
il seguente:
l et oneDB = fun ( ) −> merge 0 ; ;
3.5. ALGEBRA 35
3.5.1.8 Substitution
Il bigrafo substitution contiene dei nomi nell’interfaccia interna verso l’alto, siano
essi ~x, ed un solo nome in quella esterna verso l’alto, sia esso y. I link presenti nel
bigrafo allora sono ~x→ y.
La funzione che crea il bigrafo substitution e substitution. Essa richiede in in-
gresso la lista dei nomi dell’interfaccia interna verso l’alto ed il nome dell’interfaccia
esterna verso l’alto. La funzione crea i link ~x→ y tramite la funzione linklist .
Il codice della funzione substitution e il seguente:
l et s u b s t i t u t i o n x l y = l et subs = id 0 [ ] [ ] insubs#a d d x p l u s l i s t x l ;subs#addyplus y ;l i n k l i s t x l ” inp ” y ”onp” subs ; ;
3.5.1.9 Empty Substitution
Un bigrafo di tipo empty substitution contiene dei nomi nell’interfaccia esterna verso
l’alto ma nessun nome nell’interfaccia interna verso l’alto.
La funzione che lo crea e:
l et emptysubst i tut ion y l = l et esubs = id 0 [ ] [ ] inesubs#a d d y p l u s l i s t y l ;esubs ; ;
3.5.1.10 Fusion
Il bigrafo fusion e analogo al bigrafo substitution ma i link partono dall’interfaccia
esterna verso il basso ed arrivano all’unico nome nell’interfaccia interna verso il
basso. La funzione che lo crea e fusion ed i parametri da passare sono il nome x
dell’interfaccia interna e la lista ~y dei nomi dell’interfaccia esterna.
Il codice della funzione e il seguente:
l et f u s i o n x y l = l et f u s = id 0 [ ] [ ] inf u s#addyminus l i s t y l ;f u s#addxminus x ;l i n k l i s t y l ”onm” x ”inm” fu s ; ;
3.5.1.11 Empty Fusion
Un bigrafo di tipo empty fusion contiene dei nomi nell’interfaccia interna verso il
basso ma nessun nome nell’interfaccia esterna verso il basso.
La funzione che lo crea e:
36 CAPITOLO 3. IMPLEMENTAZIONE DEI BIGRAFI DIRETTI
l et emptyfusion x l = l et e f u s = id 0 [ ] [ ] ine f u s#addxminus l i s t x l ;e f u s ; ;
3.5.1.12 Rename
Un bigrafo di tipo rename contiene una biezione tra i nomi delle interfacce.
Vi sono due tipi di bigrafo rinomina, uno di tipo substitution ed uno di tipo
fusion. Se e di tipo substitution i link vanno dall’interfaccia interna a quella esterna,
nel caso che sia fusion i link sono orientati nel verso opposto.
Sono presenti due funzioni che creano questi due tipi di bigrafo rename, a tutti
e due va passato come parametro una lista di coppie (nome sorgente, nome destina-
zione). Questa lista viene passata alla funzione linkpairlist che si occupa di creare
i link.
Il codice delle funzioni e il seguente:
l et subs t i tut ionrename l = l et subsr = id 0 [ ] [ ] insubsr#a d d x p l u s l i s t ( f s t ( L i s t . s p l i t l ) ) ;subsr#a d d y p l u s l i s t ( snd ( L i s t . s p l i t l ) ) ;l i n k p a i r l i s t l ” inp ” ”onp” subsr ;subsr ; ;
l et fus ionrename l = l et f u s r = id 0 [ ] [ ] inf u s r#addyminus l i s t ( f s t ( L i s t . s p l i t l ) ) ;f u s r#addxminus l i s t ( snd ( L i s t . s p l i t l ) ) ;l i n k p a i r l i s t l ”onm” ”inm” f u s r ;f u s r ; ;
3.5.1.13 Discrete Ion
Il bigrafo discrete ion e formato da una radice che contiene un nodo, l’unico del
bigrafo. Il nodo a sua volta contiene un sito. I link di questo bigrafo partono dalle
porte del nodo ed arrivano alle interfacce. La funzione che costruisce questo bigrafo
e discreteion . Essa richiede come parametri:
- il nome del nodo
- la sua signature
- la lista dei nomi dell’interfaccia interna verso il basso
- la lista dei nomi dell’interfaccia esterna verso l’alto
- una lista di coppie (porta, nome) che definisce i link da creare
3.5. ALGEBRA 37
- la classe delle signature
La funzione discreteion utilizza la funzione discreteionaux per creare i link.
Il codice della funzione discreteion e il seguente:
l et d i s c r e t e i o n v k x l y l l l s i g n s =l et d i s c r i o n = oneDB ( ) ind i s c r i o n#addnode v k s i g n s ;d i s c r i o n#addxminus l i s t x l ;d i s c r i o n#a d d y p l u s l i s t y l ;d i s c r i o n#s e t i n n e r i n t n 1 ;d i s c r i o n#addprntnoderoot v 0 ;d i s c r i o n#addprnts i tenode 0 v ;d i s c r e t e i o n a u x d i s c r i o n v l l ;d i s c r i o n ; ;
Una variante di discreteion e discreteionf che utilizza una funzione per trovare la
destinazione del link che parte da una data porta. Viene usata la funzione ausiliaria
discreteionauxf per costruire, data l’arieta del nodo e la funzione che mappa le porte
nei nomi, la lista (porta, nome). Viene quindi usata discreteionaux per costruire i
link come nella versione precedente.
Il codice di discreteionf e il seguente:
l et d i s c r e t e i o n f v k x l y l f s i g n s =l et d i s c r i o n = oneDB ( ) inlet l l = d i s c r e t e i o n a u x f f ( s i g n s#a r i t y v ) 0 ind i s c r i o n#addnode v k s i g n s ;d i s c r i o n#addxminus l i s t x l ;d i s c r i o n#a d d y p l u s l i s t y l ;d i s c r i o n#addprntnoderoot v 0 ;d i s c r i o n#addprnts i tenode 0 v ;d i s c r e t e i o n a u x d i s c r i o n v l l ;d i s c r i o n ; ;
3.5.2 Forma normale discreta
La forma normale si ottiene attraverso la funzione dnf che prendendo in ingresso un
bigrafo ritorna la lista [w;d;w’] contenente le componenti della scomposizione. Per
creare questi tre bigrafi si procede nel seguente modo:
- si crea un bigrafo vuoto w a cui si aggiungono gli archi e i nomi dell’interfaccia
esterna del bigrafo passato come parametro.
- si crea un bigrafo vuoto w’ a cui si aggiungono i nomi dell’interfaccia interna
del bigrafo passato come parametro.
38 CAPITOLO 3. IMPLEMENTAZIONE DEI BIGRAFI DIRETTI
- si crea un bigrafo d che mantiene i nodi, la lista prnt, la lista ctrl, il numero
di radici e siti del bigrafo passato come parametro.
Il codice della funzione e il seguente:
l et dnf ( dbg : d i r e c t edbg ) = l et w = id 0 [ ] [ ] inlet d = new d i r e c t edbg ( dbg#getnodes ) S t r ingSe t . empty
( dbg#getprnt ) [ ] ( dbg#g e t c t r l )( dbg#get inne r in tn , ( S t r ingSe t . empty , S t r ingSe t . empty ) )( dbg#getoute r in tn , ( S t r ingSe t . empty , S t r ingSe t . empty ) ) inlet w’ = id 0 [ ] [ ] inw#a d d y p l u s l i s t ( dbg#returnyp lus ) ;w#addyminus l i s t ( dbg#returnyminus ) ;w#a d d e d g e l i s t ( dbg#returnedges ) ;w’# a d d x p l u s l i s t ( dbg#returnxp lus ) ;w’# addxminus l i s t ( dbg#returnxminus ) ;d n f l i n k ( dbg#g e t l i n k ) w d w’ 0 0 ;w : : d : : w ’ : : [ ] ; ;
La funzione che scompone i link del bigrafo originale nei link dei tre bigrafi della
forma normale e dnflink. Essa richiede come parametri la lista dei link del bigrafo
originale, i tre bigrafi w, d e w’ e due interi. Questi due interi servono per creare nomi
nuovi distinti per le interfacce tra i bigrafi. Siano X− ed X+ gli insiemi dei nomi
verso il basso e verso l’alto dell’interfaccia interna, Y − ed Y + quelli dell’interfaccia
esterna, sia p una generica porta di un nodo e sia e un generico arco.
Analizzando la lista di link si possono presentare i seguenti casi con le relative
scomposizioni e codice che lo implementa:
y → x con y ∈ Y − ed x ∈ X− : si creano due nuovi nomi wi e zi ed i link da creare
sono y → wi nel bigrafo w, wi → zi nel bigrafo d e zi → x nel bigrafo w’.
l et rec d n f l i n kl i n k (w: d i r e c t edbg ) ( dbg : d i r e c t edbg ) (w ’ : d i r e c t edbg ) i j =match l i n k with| (Name( a , b ) , NameEdge( c , d ) ) : : t a i l when b =”onm” & d =”inm”
−> w#addxminus ( ”w” ˆ( s t r i n g o f i n t i ) ) ;dbg#addxminus ( ”z” ˆ( s t r i n g o f i n t j ) ) ;dbg#addyminus ( ”w” ˆ( s t r i n g o f i n t i ) ) ;w’#addyminus ( ”z” ˆ( s t r i n g o f i n t j ) ) ;w#addlinkfromname a ”onm” ( ”w” ˆ( s t r i n g o f i n t i ) ) ”inm” ;dbg#addlinkfromname ( ”w” ˆ( s t r i n g o f i n t i ) ) ”onm”( ”z” ˆ( s t r i n g o f i n t j ) ) ”inm” ;w’#addlinkfromname ( ”z” ˆ( s t r i n g o f i n t j ) ) ”onm” c ”inm” ;d n f l i n k t a i l w dbg w’ ( i +1) ( j +1)
x→ y con y ∈ Y + ed x ∈ X+ : si creano due nuovi nomi wi e zi ed i link da creare
sono wi → y nel bigrafo w, zi → wi nel bigrafo d e x→ zi nel bigrafo w’.
3.5. ALGEBRA 39
| (Name( a , b ) , NameEdge ( c , d ) ) : : t a i l when b =” inp ” & d =”onp”−> w#addxplus ( ”w” ˆ( s t r i n g o f i n t i ) ) ;dbg#addxplus ( ”z” ˆ( s t r i n g o f i n t j ) ) ;
dbg#addyplus ( ”w” ˆ( s t r i n g o f i n t i ) ) ;w’#addyplus ( ”z” ˆ( s t r i n g o f i n t j ) ) ;
w#addlinkfromname ( ”w” ˆ( s t r i n g o f i n t i ) ) ” inp ” c ”onp” ;dbg#addlinkfromname ( ”z” ˆ( s t r i n g o f i n t j ) ) ” inp ”( ”w” ˆ( s t r i n g o f i n t i ) ) ”onp” ;w’#addlinkfromname a ” inp ” ( ”z” ˆ( s t r i n g o f i n t j ) ) ”onp” ;d n f l i n k t a i l w dbg w’ ( i +1) ( j +1)
y → e con y ∈ Y − : si crea il link y → e in w.
| (Name( a , b ) , NameEdge( c , d ) ) : : t a i l when b =”onm” & d =”e”−> w#addlinkfromname a ”onm” c ”e” ;d n f l i n k t a i l w dbg w’ i j
x→ e con x ∈ X+ : si creano due nuovi nomi wi e zi ed i link da creare sono wi → e
nel bigrafo w, zi → wi nel bigrafo d e x→ zi nel bigrafo w’.
| (Name( a , b ) , NameEdge( c , d ) ) : : t a i l when b =” inp ” & d =”e”−> w#addxplus ( ”w” ˆ( s t r i n g o f i n t i ) ) ;dbg#addxplus ( ”z” ˆ( s t r i n g o f i n t j ) ) ;dbg#addyplus ( ”w” ˆ( s t r i n g o f i n t i ) ) ;w’#addyplus ( ”z” ˆ( s t r i n g o f i n t j ) ) ;w#addlinkfromname ( ”w” ˆ( s t r i n g o f i n t i ) ) ” inp ” c ”e” ;dbg#addlinkfromname ( ”z” ˆ( s t r i n g o f i n t j ) ) ” inp ”( ”w” ˆ( s t r i n g o f i n t i ) ) ”onp” ;w’#addlinkfromname a ” inp ” ( ”z” ˆ( s t r i n g o f i n t j ) ) ”onp” ;d n f l i n k t a i l w dbg w’ ( i +1) ( j +1)
p→ y con y ∈ Y + : si crea un nuovo nome wi ed i link da creare sono wi → y nel
bigrafo w e p→ wi nel bigrafo d.
| ( Port ( a , b ) , NameEdge( c , d ) ) : : t a i l when d =”onp”−> w#addxplus ( ”w” ˆ( s t r i n g o f i n t i ) ) ;dbg#addyplus ( ”w” ˆ( s t r i n g o f i n t i ) ) ;w#addlinkfromname ( ”w” ˆ( s t r i n g o f i n t i ) ) ” inp ” c ”onp” ;dbg#addl inkfromport a b ( ”w” ˆ( s t r i n g o f i n t i ) ) ”onp” ;d n f l i n k t a i l w dbg w’ ( i +1) j
p→ x con x ∈ X− : si crea un nuovo nome zi ed i link da creare sono p → zi nel
bigrafo d e zi → x nel bigrafo w’.
40 CAPITOLO 3. IMPLEMENTAZIONE DEI BIGRAFI DIRETTI
| ( Port ( a , b ) , NameEdge( c , d ) ) : : t a i l when d =”inm”−> dbg#addxminus ( ”z” ˆ( s t r i n g o f i n t j ) ) ;w’#addyminus ( ”z” ˆ( s t r i n g o f i n t j ) ) ;dbg#addl inkfromport a b ( ”z” ˆ( s t r i n g o f i n t j ) ) ”inm” ;w’#addlinkfromname ( ”z” ˆ( s t r i n g o f i n t j ) ) ”onm” c ”inm” ;d n f l i n k t a i l w dbg w’ i ( j +1)
p→ x : si crea un nuovo nome wi ed i link da creare sono wi → e nel bigrafo w e
p→ wi nel bigrafo d.
| ( Port ( a , b ) , NameEdge( c , d ) ) : : t a i l when d =”e”−> w#addxplus ( ”w” ˆ( s t r i n g o f i n t i ) ) ;dbg#addyplus ( ”w” ˆ( s t r i n g o f i n t i ) ) ;w#addlinkfromname ( ”w” ˆ( s t r i n g o f i n t i ) ) ” inp ” c ”e” ;dbg#addl inkfromport a b ( ”w” ˆ( s t r i n g o f i n t i ) ) ”onp” ;d n f l i n k t a i l w dbg w’ ( i +1) j
| head : : t a i l −> d n f l i n k t a i l w dbg w’ i j| [ ] −> ( ) ; ;
3.5.2.1 Scomposizione in bigrafi primi
Una volta creato d con la funzione dnf e possibile scomporlo in bigrafi primi con la
funzione primebg. Questa funzione prendendo in ingresso un bigrafo restituisce una
lista di bigrafi. Il primo elemento di questa lista e un bigrafo di tipo rename che
contiene i link del bigrafo passato che vanno da un’interfaccia all’altra. Seguono a
questo tanti bigrafi quante le radici del bigrafo passato, ognuno con una sola radice.
Questi sono il risultato della scomposizione del bigrafo passato, per ogni sua radice
si crea un bigrafo che contiene i suoi figli ed i link che partono da questi. Il codice
di primebg e il seguente:
l et primebg ( dbg : d i r e c t edbg ) = l et alpha = id 0 [ ] [ ] inprimealpha ( dbg#g e t l i n k ) alpha ;alpha : : ( p r i m e b g l i s t ( dbg#getprnt ) ( dbg#g e t l i n k )
( dbg#g e t c t r l ) ( dbg#g e t o u t e r i n t n − 1) 0 ) ; ;
La funzione primealpha crea il bigrafo rinomina mentre primebglist crea i bigrafi a
partire dalle componenti del bigrafo passato.
3.5.3 Uguaglianza tra bigrafi
La funzione che controlla se due bigrafi sono uguali e samebg, essa non fa altro
che controllare se tutte le componenti del bigrafo sono uguali. In questo caso si
sfrutta il polimorfismo delle funzioni di OCaml in quanto l’operatore = funziona
su qualsiasi tipo e ricorsivamente anche sulle liste. Per poter confrontare le liste
3.5. ALGEBRA 41
pero bisogna ordinarle in quanto gli stessi elementi possono generare liste diverse a
seconda dell’ordine. Il modulo List contiene una funzione sort che necessita pero di
una funzione per confrontare gli elementi della lista da ordinare. Anche in questo
caso si sfrutta il polimorfismo, in particolare della funzione compare che garantisce
un ordine fra gli elementi.
Il codice della funzione e il seguente :
l et samebg ( dbg1 : d i r e c t edbg ) ( dbg2 : d i r e c t edbg ) =( St r ingSe t . equal ( dbg1#getnodes ) ( dbg2#getnodes ) ) &( St r ingSe t . equal ( dbg1#getedges ) ( dbg2#getedges ) ) &( ( L i s t . s o r t compare dbg1#getprnt ) =
( L i s t . s o r t compare ( dbg2#getprnt ) ) ) &( ( L i s t . s o r t compare dbg1#g e t l i n k ) =
( L i s t . s o r t compare ( dbg2#g e t l i n k ) ) ) &( ( L i s t . s o r t compare dbg1#g e t c t r l ) =( L i s t . s o r t compare ( dbg2#g e t c t r l ) ) ) &( ( dbg1#g e t i n n e r i n t n ) = ( dbg2#g e t i n n e r i n t n ) ) &( St r ingSe t . equal ( dbg1#getxminus ) ( dbg2#getxminus ) ) &( St r ingSe t . equal ( dbg1#getxp lus ) ( dbg2#getxp lus ) ) &( ( dbg1#g e t o u t e r i n t n ) = ( dbg2#g e t o u t e r i n t n ) ) &( St r ingSe t . equal ( dbg1#getyminus ) ( dbg2#getyminus ) ) &( St r ingSe t . equal ( dbg1#getyp lus ) ( dbg2#getyp lus ) ) ; ;
4Il linguaggio ed il compilatore
In questo capitolo sara presentato il linguaggio per descrivere i bigrafi diretti e il re-
lativo compilatore. Tale linguaggio, denominato DBPL (Directed Bigraph Program-
ming Language) consente di descrivere sia un bigrafo sia la signature di controllo.
Il compilatore puo generare sia le classi directedbg e signatures, sia un programma
OCaml equivalente.
4.1 Il linguaggio
Il linguaggio DBPL consente di
• Definire la signature di controllo di un bigrafo
• Scrivere in modo semplice i bigrafi elementari
• Applicare le operazione tra bigrafi
• Dare un nome ad un bigrafo per riusarlo in seguito
4.1.1 La grammatica
Il linguaggio e descritto dalla seguente grammatica libera dal contesto:⟨dbpl
⟩::=
⟨signature
⟩⟨letblock
⟩EOF⟨
signature⟩
::= SIGNATURE LSB RSB SEMICOLON
| SIGNATURE LSB⟨sigelem
⟩RSB SEMICOLON⟨
sigelem⟩
::= SIGTYPE NAME COLON NUM
|⟨sigelem
⟩COMMA SIGTYPE NAME COLON NUM⟨
letblock⟩
::=⟨bg⟩
|⟨letelem
⟩IN⟨letblock
⟩
44 CAPITOLO 4. IL LINGUAGGIO ED IL COMPILATORE
⟨letelem
⟩::= LET NAME EQ
⟨bg⟩
⟨bg⟩
::= LRB⟨bg⟩
RRB
|⟨merge
⟩|⟨ion⟩
|⟨substitution
⟩|⟨fusion
⟩|⟨closure
⟩|⟨permutation
⟩|⟨tensor
⟩|⟨osp⟩
|⟨isp⟩
|⟨sp⟩
|⟨posp
⟩|⟨psp⟩
|⟨composition
⟩| NAME
⟨merge
⟩::= MERGE NUM
⟨ion⟩
::= NAME⟨ctrllist
⟩⟨substitution
⟩::= NAME SUBSTITUTION NAME
| NAME SUBSTITUTION⟨nset
⟩| NAME SUBSTITUTION
|⟨nset
⟩SUBSTITUTION
⟨fusion
⟩::= NAME FUSION NAME
|⟨nset
⟩FUSION NAME
| FUSION NAME
| FUSION⟨nset
⟩⟨closure
⟩::= NAME CLOSURE NAME
|⟨nset
⟩CLOSURE NAME
| NAME CLOSURE⟨nset
⟩|⟨nset
⟩CLOSURE
⟨nset
⟩| NAME CLOSURE
| CLOSURE NAME
4.2. IL COMPILATORE 45
|⟨nset
⟩CLOSURE
| CLOSURE⟨nset
⟩⟨permutation
⟩::= PERMUTATION
⟨siteset
⟩⟨tensor
⟩::=
⟨bg⟩
TENSOR⟨bg⟩
⟨osp
⟩::=
⟨bg⟩
OSP⟨bg⟩
⟨isp⟩
::=⟨bg⟩
ISP⟨bg⟩
⟨sp⟩
::=⟨bg⟩
SP⟨bg⟩
⟨posp
⟩::=
⟨bg⟩
POSP⟨bg⟩
⟨psp
⟩::=
⟨bg⟩
PSP⟨bg⟩
⟨composition
⟩::=
⟨bg⟩
COMPOSITION⟨bg⟩
⟨ctrllist
⟩::= LSB RSB
| LSB⟨ctrlelem
⟩RSB⟨
ctrlelem⟩
::= SIGN NAME
|⟨ctrlelem
⟩COMMA SIGN NAME⟨
siteset⟩
::= LSB RSB
| LSB⟨siteelem
⟩RSB⟨
siteelem⟩
::= NUM
|⟨siteelem
⟩COMMA NUM⟨
nset⟩
::= LSB⟨nsetelem
⟩RSB⟨
nsetelem⟩
::= NAME COMMA NAME
|⟨nsetelem
⟩COMMA NAME
Per convenzione i simboli terminali sono scritti in maiuscolo, mentre i simboli non
terminali in minuscolo. I simboli non terminali sono anche racchiusi tra parentesi
angolari. Il simbolo iniziale e “dbpl”.
4.2 Il Compilatore
Il compilatore per il linguaggio DBPL e formato da tre componenti: un analizzatore
lessicale, un parser ed un insieme di funzioni che formano l’interfaccia.
46 CAPITOLO 4. IL LINGUAGGIO ED IL COMPILATORE
4.2.1 L’analizzatore lessicale
L’analizzatore lessicale si occupa di analizzare il sorgente DBPL (leggendolo da un
file o una stringa, a seconda di quale funzione di interfaccia e stata usata) e produrre
una lista di token. L’analizzatore solleva una eccezione se incontra una sequenza di
caratteri che non corrisponde a nessun token conosciuto. L’analizzatore e stato
scritto usando il programma ocamllex. Ocamllex1 legge da un file le regole per
riconoscere i token (scritte usando espressioni regolari) e produce il sorgente del
corrispondende analizzatore lessicale scritto in OCaml. Queste sono le associazioni
tra stringhe e tokens:
• SIGNATURE : “Signature”
• LSB: “[”
• RSB: “]”
• SEMICOLON: “;”
• SIGTYPE : “active” oppure “passive” oppure “atomic”
• NAME : qualsiasi stringa che inizia con una lettera minuscola seguita da un
qualsiasi numero di lettere minuscole, maiuscole (esclusa la X) o cifre
• COLON : “:”
• NUM : un qualsiasi numero intero assoluto (non puo iniziare con uno 0)
• COMMA : “,”
• IN : “in”
• LET : “let”
• EQ : “=”
• LRB : “(”
• RRB : “)”
• MERGE : “merge”
• SUBSTITUTION : “/”
1Si veda la sezione 4.3.1 per ulteriori dettagli
4.2. IL COMPILATORE 47
• FUSION : \
• CLOSURE : “X”
• PERMUTATION : “@”
• TENSOR : “*”
• OSP : “/\”
• ISP : “\/”
• SP : “||”
• POSP : “/ˆ\”
• PSP : “|”
• COMPOSITION “°”
Una qualsiasi stringa che non rientra in queste categorie e considerata un errore.
In questo caso l’analizzatore lessicale alza una eccezione di tipo Unknown Symbol
con due parametri: la riga e la colonna dove e stata incontrata la stringa.
4.2.2 Il parser
Il parser si occupa di analizzare una sequenza di token (ottenuti invocando l’analizza-
tore lessicale) per determinare la sua struttra grammaticale in base alla grammatica
definita nella sezione 4.1.1. Il parser e stato scritto usando il programma ocamlyacc.
Ocamlyacc2 legge da un file la descrizione di una grammatica libera dal contesto
LARL(1) e produce un parser per quella grammatica scritto in OCaml.
4.2.2.1 La signature
Il parser controlla che ogni sorgente DBPL inizi con la definizione di una signature.
Questa descrizione e introdotta dalla parola chiave “Signature” seguita da zero o
piu definizioni di tipi di signature. Tali definizioni sono racchiuse da una coppia
di parentesi quadre e separate da virgole. La definizione di un tipo di signature e
formata da tre elementi: un tipo di attivita (active, passive o atomic), un nome per
questo tipo e un numero che rappresenta l’arieta. Esempi di signature valide sono:
2Si veda la sezione 4.3.2 per ulteriori dettagli
48 CAPITOLO 4. IL LINGUAGGIO ED IL COMPILATORE
Signature [];
Signature [active t0:2];
Signature [atomic t1:1,active t2:4];
Il parser e in grado di capire quando la definizione di una signature e scritta male.
In questo caso solleva un’apposita eccezione che indica il punto in cui si e verificato
l’errore. Se la signature e stata definita correttamente i singoli tipi sono inseriti in
un oggetto di tipo signatures.
4.2.2.2 I bigrafi elementari
Una volta definita la signature (che puo essere vuota) e possibile descrivere un qual-
siasi bigrafo utilizzando i bigrafi elementari le operazioni tra bigrafi. Dbpl consente
di descrivere i bigrafi substitution, fusion, closure, merge, permutation e discrete ion.
La definizione di questi bigrafi non e associativa e ha la precedenza sulle operazioni
tra bigrafi. Per descrivere questi bigrafi e necessario introdurre i name set, i site set
e le control list.
Name set I name set sono semplicemente delle liste di nomi di interfacce. Ogni
name set deve contenere almeno due nomi ed essere racchiuso tra parentesi quadre.
I singoli nomi sono separati da virgole e sono token di tipo NAME. Esempi di name
set validi sono:
[a,b]
[a,b,c]
[a1,bB]
Site set I site set sono liste di siti utilizzati per la descrizione dei bigrafi permuta-
tion. Un siteset e una lista di token di tipo NUM racchiusa da parentesi quadre. Gli
elementi della lista sono separati da virgole. Un site set puo essere vuoto. Esempi
di siteset validi sono:
[]
[1]
[2,5,4,1]
Control list Le control list descrivono i link che partono dal nodo contenuto in
un discrete ion. Una control list e una lista, racchiusa tra parentesi quadre, di token
4.2. IL COMPILATORE 49
di tipo NAME preceduti dai simboli “+” o “-”. Gli elementi di questa lista (che puo
essere vuota) sono separati da virgole. Esempi di control list valide sono:
[]
[+a]
[-a,+b,-c,+d]
Per convenzione i nomi e i name set posizionati a sinistra del simbolo che rap-
presenta il bigrafo elementare faranno parte dell’interfaccia esterna del bigrafo. I
nomi e i name set posizionati a destra invece faranno parte dell’interfaccia interna.
Quando si scrivera “uno o piu nomi” si intendera un nome oppure un name set.
Bigrafo substitution Il bigrafo substitution contiene uno o piu nomi nell’inter-
faccia interna verso l’alto che sono collegati ad un unico nome nell’interfaccia esterna
verso l’alto. Se si omettono i nomi dell’interfaccia interna verso l’alto si ottiene il
bigrafo emptysubstitution. La sintassi per descrivere un bigrafo di questo tipo e:
NAME / NAME
NAME / nset
NAME /
nset /
Esempi di bigrafi substitution validi sono:
a/b
a/[b,c]
a/
[b,c]/
In figura 4.1 sono riportati degli esempi di questo tipo di bigrafo. Sotto ogni
bigrafo c’e il codice DBPL per ottenerlo.
Bigrafo fusion Il bigrafo fusion contiene uno o piu nomi nell’interfaccia esterna
verso il basso che sono collegati ad un unico nome nell’interfaccia interna verso il
basso. Se si omettono i nomi dell’interfaccia esterna verso il basso si ottiene il bigrafo
emptyfusion. La sintassi per descrivere un bigrafo di questo tipo e:
NAME \ NAME
nset \ NAME
50 CAPITOLO 4. IL LINGUAGGIO ED IL COMPILATORE
a
ba/b
a
b c da/[b,c,d]
b c d
[b,c,d]/
Figura 4.1: Esempi di bigrafi substitution
a
ba\b
a
b c d
a\[b,c,d]b c d\[b,c,d]
Figura 4.2: Esempi di bigrafi fusion
\ NAME
\ nset
Esempi di bigrafi fusion validi sono:
a\ b
[a,b]\ c
\ a
\ [a,b]
In figura 4.2 sono riportati degli esempi di questo tipo di bigrafo. Sotto ogni bigrafo
c’e il codice DBPL per ottenerlo.
Bigrafo closure Il bigrafo closure contiene uno o piu nomi nell’interfaccia esterna
verso il basso e uno o piu nomi nell’interfaccia interna verso l’alto. Tutti questi nomi
sono collegati ad un unico arco. Si possono omettere i nomi dell’interfaccia esterna
verso il basso per ottenere un bigrafo down-closure oppure i nomi dell’interfaccia
interna verso l’alto per ottenere un bigrafo up-closure. La sintassi per descrivere un
bigrafo di questo tipo e:
4.2. IL COMPILATORE 51
a
b
e
aXb
a b
e
c d[a,b]X[c,d]
a b
e
[a,b]Xc d
e
X[c,d]
Figura 4.3: Esempi di bigrafi closure
NAME X NAME
nset X NAME
NAME X nset
nset X nset
NAME X
X NAME
nset X
X nset
Esempi di bigrafi closure validi sono:
a X b
[a,b] X c
a X [b,c]
[a,b] X [c,d]
a X
X a
[a,b] X
X [a,b]
In figura 4.3 sono riportati degli esempi di questo tipo di bigrafo. Sotto ogni bigrafo
c’e il codice DBPL per ottenerlo.
Bigrafo merge Il bigrafo merge e formato da una radice che contiene n siti al
suo interno. La sintassi per descrivere un bigrafo di questo tipo e:
merge NUM
Esempi di bigrafi merge validi sono:
52 CAPITOLO 4. IL LINGUAGGIO ED IL COMPILATORE
0
0
merge 1
0
0 1
merge 2
Figura 4.4: Esempi di bigrafi merge
0 1 2
2 0 1
@[2,0,1]
Figura 4.5: Esempio di bigrafo permutation
merge 1
merge 2
merge 6
In figura 4.4 sono riportati degli esempi di questo tipo di bigrafo. Sotto ogni bigrafo
c’e il codice DBPL per ottenerlo.
Bigrafo permutation Il bigrafo permutation e formato da n radici e n siti e ogni
radice ha come figlio un solo sito. Il numero del sito figlio e determinato da un site
set. Il site set usa una notazione posizionale: il sito che ha come numero il primo
elemento del site set sara figlio della radice 0, il sito che ha come numero il secondo
elemento del site set sara figlio della radice 1 e cosı via. La sintassi per descrivere
un bigrafo di questo tipo e:
@ siteset
Esempi di bigrafi permutation validi sono:
@ [1,4,2,3]
@ [5,1,3]
In figura 4.5 e riportato un esempo di questo tipo di bigrafo. Sotto ogni bigrafo c’e
il codice DBPL per ottenerlo.
4.2. IL COMPILATORE 53
0
v00
a c
b d
t2 [-a,+b,-c,+d]
Figura 4.6: Esempio di bigrafo discrete ion
Bigrafo discrete ion Il bigrafo discrete ion e formato da una radice che con-
tiene un nodo. Il nodo contiene un sito. Dalle porte del nodo partono dei link ed
arrivano alle interfacce. I link del nodo sono descritti dalla control list la quale usa
una notazione posizionale simile al site set. Il primo elemento della control list e
un link che parte dalla porta 0 del nodo, il secondo e un link che parte dalla porta
1 e cosı via. Ogni elemento della control list inizia con un simbolo “+” o “-”. Gli
elementi che iniziano con un “+” sono collegati al corrispondente nome sull’inter-
faccia esterna, mentre quelli che iniziano con un “-” sono collegati al corrispondente
nome sull’interfaccia interna. Ogni nodo ha una arieta e puo essere active, passi-
ve o atomic. Queste proprieta sono descritte dalla signature definita all’inizio del
programma DBPL. Il nome del tipo di un elemento della signature e usato quando
bisogna descrivere un discrete ion. La sintassi per descrivere un bigrafo di questo
tipo e:
NAME ctrlist
dove NAME e il nome di un tipo di signature precedentemente definito Esempi di
bigrafi discrete ion validi sono:
t0 [+a]
t1 [-a]
t2 [-a,+b,+d,-c]
In figura 4.6 e riportato un esempo di questo tipo di bigrafo. Sotto ogni bigrafo c’e
il codice DBPL per ottenerlo.
54 CAPITOLO 4. IL LINGUAGGIO ED IL COMPILATORE
4.2.2.3 Le operazioni tra bigrafi
Una volta che si ha a disposizione i bigrafi elementari e possibile applicare le ope-
razioni tra bigrafi per ottenere un qualsiasi altro bigrafo. Le operazioni tra bigrafi
supportate dal DBPL sono:
• Composizione
• Outer sharing product
• Inner sharing product
• Sharing product
• Prime outer sharing product
• Prime sharing product
• Tensore
Queste operazioni sono elencate in ordine decrescente di precedenza e sono associa-
tive (per convenzione si associa a sinistra).
Tensore Il tensore e l’operazione che “unisce” due bigrafi disgiunti. La sintassi
per questa operazione e:
bg * bg
dove bg sono due bigrafi disgiunti. Se i due bigrafi non sono disgiunti il parser alza
una eccezzione Bigraphs are not disjoint. Esempi di tensore sono:
marge 1 * a/b
t1 [-a,+b,-c,+d] * [f,g] X z
In figura 4.7 e riportato un esempo di questo tipo di bigrafo. Sotto ogni bigrafo c’e
il codice DBPL per ottenerlo.
Composizione Informalmente questa operazione tra due bigrafi inserisce le radici
del secondo nei siti del primo. Inoltre modifica i links che attraversano l’interfaccia
comune (quella interna del primo e quella esterna del secondo) cambiando le loro
destinazioni (o eliminandoli). Per effettuare questa operazione e necessario che i nodi
e gli archi dei due bigrafi siano disgiunti. Inoltre l’interfaccia interna del primo deve
essere uguale all’interfaccia esterna del secondo. La sintassi per questa operazione
e:
4.2. IL COMPILATORE 55
0
v00
a c
b d
t2 [-a,+b,-c,+d]
f g
e
z[c,d]Xz
0
v0
0
a c
b d f g
e
z
t2 [-a,+b,-c,+d] * [f,g]Xz
Figura 4.7: Esempio di tensore: sopra i bigrafi di partenza, sotto il risultato
56 CAPITOLO 4. IL LINGUAGGIO ED IL COMPILATORE
0
v00
c d
a b
t2 [+a,-c,+b,-d]
0
v1
f
c d
g ht1 [-f] * c\g * d\h
0
v0v1
g h
a b
f
t2 [+a,-c,+b,-d] °(t1 [-f] * c\g * d\h)
Figura 4.8: Esempio di composizione: sopra i bigrafi di partenza, sotto il risultato
4.2. IL COMPILATORE 57
b
a
a/b
c
a
a/c
a
b c
a/b /\a/b
Figura 4.9: Esempio di outer sharing product: sopra i bigrafi di partenza, sotto ilrisultato
bg ° bg
dove bg sono i due bigrafi. Esempi di composizione sono:
[a,b,c]\z ° zXf
t2 [+a,+b,-c,-d] ° (t1 [-f] * c\g * d\h)
In figura 4.8 e riportato un esempo di questo tipo di bigrafo. Sotto ogni bigrafo c’e
il codice DBPL per ottenerlo.
Outer sharing product L’outer sharing product e una variante del tensore che
consente la condivisione dei nomi dell’interfaccia esterna verso l’alto. La sintassi per
questa operazione e:
bg /\ bg
Esempi di outer sharing product sono:
a/b /\ a/c
t1 [+a] /\ t1 [+a]
58 CAPITOLO 4. IL LINGUAGGIO ED IL COMPILATORE
a
b
b/a
a
c
c/a
a
b c
b/a \/ c/a
Figura 4.10: Esempio di inner sharing product: sopra i bigrafi di partenza, sotto ilrisultato
In figura 4.9 e riportato un esempo di questo tipo di bigrafo. Sotto ogni bigrafo c’e
il codice DBPL per ottenerlo.
Inner sharing product L’inner sharing product e una variante del tensore che
consente la condivisione dei nomi dell’interfaccia interna verso il basso. La sintassi
per questa operazione e:
bg \/ bg
Esempi di inner sharing product sono:
b/a \/ c/a
t1 [-a] \/ t1 [-a]
In figura 4.10 e riportato un esempo di questo tipo di bigrafo. Sotto ogni bigrafo c’e
il codice DBPL per ottenerlo.
bigrafo. Sotto ogni bigrafo c’e il codice DBPL per ottenerlo.
4.2. IL COMPILATORE 59
a
d
c
b
a/d * c\b
e
a
b
f
a/e * f\b
c f a
b d e
(a/d * c\b) || (a/e * f\b)
Figura 4.11: Esempio di sharing product: sopra i bigrafi di partenza, sotto il risultato
Sharing product Il sharing product e una variante del tensore che consente la
condivisione dei nomi dell’interfaccia interna verso il basso e dei nomi dell’interfaccia
esterna verso l’alto. La sintassi per questa operazione e:
bg || bg
Esempi di sharing product sono:
b/a || c/a
t1 [-a,+b] || t1 [-a,+b]
In figura 4.11 e riportato un esempo di questo tipo di bigrafo. Sotto ogni bigrafo c’e
il codice DBPL per ottenerlo.
Prime outer sharing product Il prime outer sharing product e una variante
del tensore che consente la condivisione dei nomi dell’interfaccia esterna verso l’alto
e tutti gli elementi del place graph figli di radici diventano figli della stessa radice.
La sintassi per questa operazione e:
bg /^\ bg
60 CAPITOLO 4. IL LINGUAGGIO ED IL COMPILATORE
0
v0
c
t1 [+c]
1
v1
c
t1 [+c]
0
v0 v1
c
t1 [+c] /ˆ\t1 [+c]
Figura 4.12: Esempio di prime outer sharing product: sopra i bigrafi di partenza,sotto il risultato
4.2. IL COMPILATORE 61
0
v0
a
bt1 [+a,-b]
1
v1
a
bt1 [+a,-b]
0
v0 v1
a
bt1 [+a,-b] — t1 [+a,-b]
Figura 4.13: Esempio di prime sharing product: sopra i bigrafi di partenza, sotto ilrisultato
Un esempio di prime outer sharing product e:
t1 [+a] /^\ t1 [+a]
In figura 4.12 e riportato un esempo di questo tipo di bigrafo. Sotto ogni bigrafo c’e
il codice DBPL per ottenerlo.
Prime sharing product Il prime sharing product e una variante del tensore
che consente la condivisione dei nomi dell’interfaccia esterna verso l’alto, dei nomi
dell’interfaccia interna verso il basso e tutti gli elementi del place graph figli di radici
diventano figli della stessa radice. La sintassi per questa operazione e:
bg | bg
Un esempio di prime outer sharing product e:
t1 [+a,-b] | t1 [+a,-b]
62 CAPITOLO 4. IL LINGUAGGIO ED IL COMPILATORE
In figura 4.13 e riportato un esempo di questo tipo di bigrafo. Sotto ogni bigrafo c’e
il codice DBPL per ottenerlo.
4.2.2.4 Forzare le precedenze
Nel caso ci sia la necessita si possono usare le parentesi tonde per cambiare l’ordine
di esecuzione delle operazioni in un ordine diverso da quello dettato dalle normali
precedenze tra gli operatori. Ad esempio:
t2[-a,-b]°(merge 1 *a\c*b\d)
eseguira i tensori prima della composizione.
4.2.2.5 I blocchi let
Il linguaggio DBPL consente di dare dei nomi a bigrafi per poi riutilizzarli senza
dover riscrivere il bigrafo stesso. Come in molti linguaggi di programmazione l’as-
sociazione di un nome ad un bigrafo avviene usando le parole chiave “let” e “in”; in
particolare la sintassi e:
let NAME = bg IN letblock
dove NAME e il nome per il nuovo bigrafo e bg e il bigrafo da nominare (e possibile
creare degli alias per lo stesso bigrafo). Il letblock finale permette di un annidamento
arbitrario dei blocchi let. Esempi:
let bg1=[a,b]\c in
let bg2=[f,g]X[h,j] in
bg1 * bg2
4.2.3 L’interfaccia del compilatore
Il compilatore puo essere invocato attraverso quattro diverse funzioni che determi-
nano che parametri prendere e che risultati ritornare. Tutte queste funzioni sono
contenute nel modulo Dbplcompiler. Le funzioni sono queste:
compilefromfile: string -> Bigraph.signatures * Bigraph.directedbg
Questa funzione prende in ingresso una striga che contiene il nome di un file.
La funzione legge da questo file il sorgente dbpl e restituisce la coppia (signature,
bigrafo).
4.3. ASPETTI IMPLEMENTATIVI 63
compilefromstring : string -> Bigraph.signatures * Bigraph.directedbg
Questa funzione prende in ingresso una striga che contiene il sorgente dbpl e
restituisce la coppia (signature, bigrafo)
compilefromfile2ocaml : string -> string -> unit
Questa funzione prende in ingresso due strighe. La prima contiene il nome del
file col sorgente dbpl da elaborare. La seconda e il nome del file in cui scrivere il
programma ocaml corrispondente.
compilefromstring2ocaml : string -> string -> unit
Questa funzione prende in ingresso due strighe. La prima contiene il sorgente
dbpl e la seconda e il nome del file in cui scrivere il programma ocaml corrispondente.
4.3 Aspetti implementativi
In questa sezione sara descritta l’implementazione delle componenti del compilato-
re. La generazione del programma ocaml corrispondente al bigrafo e molto simile
alla generazione della coppia (signature, bigrafo) e quindi saranno descritte solo le
differenze rispetto a quest’ultima.
4.3.1 L’analizzatore lessicale
L’analizzatore lessicale e stato generato usando il programma ocamllex. Questo
programma prende un file sorgente contenente le regole per individuare i token e
prodice il sorgente ocaml di un programma che implementa l’analizzatore lessicale.
Il sorgente ocamllex e diviso in quattro sezioni: intestazione, definizioni, regole e
il blocco finale. Solo la prima e la terza sezione sono obbligatorie, le altre sono
opzionali. In questa implementazione dell’analizzatore lessicale non e stato usato il
blocco finale
4.3.1.1 Intestazione
L’intestazione contiene codice ocaml racchiuso tra parentesi graffe ed e copiata al-
l’inizio del codice ocaml generato da ocamllex. Questa sezione di solito e usata per
aprire moduli oppure per definire funzioni da usare successivamente. L’intestazione
dell’analizzatore lessicale e:
{
open Dbplparser
64 CAPITOLO 4. IL LINGUAGGIO ED IL COMPILATORE
exception Unknown_Symbol of (int*int);;
let incr_linenum lexbuf = let pos = lexbuf.Lexing.lex_curr_p in
lexbuf.Lexing.lex_curr_p <- { pos with
Lexing.pos_lnum = pos.Lexing.pos_lnum + 1;
Lexing.pos_bol = pos.Lexing.pos_cnum;
}
;;
}
In questo caso per prima cosa si apre il modulo Dbplparser che contiene l’implemen-
tazione del parser. Tale apertura e necessaria per poter accedere alla lista di token
previsti per il linguaggio. Dopo l’apertura del modulo c’e la dichiarazione di una ec-
cezione. Questa e l’eccezione sollevata dall’analizzatore quando incontra una stringa
che non corrisponde ad un token valido. Infine c’e la dichiarazione della funzione
incr linenum. Questa funzione si occupa di aggiornare alcune tabelle necessarie per
localizzare in quale punto del sorgente dbpl si incontrano i vari token. Tali tabelle
sono indispensabili per riconoscere gli errori e sollevare le giuste eccezioni.
4.3.1.2 Definizioni
Questa sezione serve per dare dei nomi a dei pattern per semplificare la scrittura
dell’analizzatore. La sintassi generica di una definizione e
let ident = regexp
dove ident e il nome della definizione e regexp e una espressione regolare. La lista
delle definizioni e:
let num = [’0’-’9’] | [’1’-’9’][’0’-’9’]+
let name = [’a’-’z’][’a’-’z’ ’A’-’W’ ’Y’-’Z’ ’0’-’9’]*
let sign = ’+’ | ’-’
secondo queste definizioni num e una stringa formata dai caratteri compresi tra 0
e 9 oppure dai caratteri compresi tra 1 e 9 seguiti da uno o piu caratteri tra 0 e
9. name e una stringa che inizia con una lettera minuscola ed e seguita da zero o
piu lettere minuscole, maiuscole (esclusa la X) e cifre. Infine sign sono i caratteri +
oppure -.
4.3. ASPETTI IMPLEMENTATIVI 65
4.3.1.3 Regole
Le regole definiscono cosa bisogna fare quando si trova una certa stringa. La sintassi
generica per una regola e:
rule entrypoint [arg1... argn] = parse
| pattern {action}
| pattern {action}
...
entrypoint e il nome della regola (che puo avere parametri). Questo sara anche il
nome della funzione da chiamare nel parser per chiamare l’analizzatore. pattern e
una espressione regolare (si possono usare le definizioni di prima) e action e l’azione
eseguita quando si incontra una stringa che corrisponde al pattern. L’analizzatore
legge un carattere alla volta fino a quando non trova un pattern che corrisponde alla
stringa letta. Se ci sono piu pattern che corrispondono si sceglie il piu lungo, se ci
sono piu pattern dalla stessa lunghezza si sceglie il primo. Le regole dell’analizzatore
sono queste:
rule token = parse
| [’ ’ ’\t’] { token lexbuf }
| ’\n’ { incr_linenum lexbuf; token lexbuf }
| "merge" { MERGE }
| "Signature" { SIGNATURE }
| "active"
| "passive"
| "atomic" as sigtype { SIGTYPE (sigtype) }
| "in" { IN }
| "let" { LET }
| num as n { NUM(int_of_string n) }
| name as nm { NAME(nm) }
| sign as sn { SIGN (String.make 1 sn) }
| "=" { EQ }
| "[" { LSB }
| "]" { RSB }
| "," { COMMA }
| ":" { COLON }
| ";" { SEMICOLON }
66 CAPITOLO 4. IL LINGUAGGIO ED IL COMPILATORE
| "*" { TENSOR }
| "°" { COMPOSITION }
| "/\\" { OSP }
| "\\/" { ISP }
| "||" { SP }
| "/^\\" { POSP }
| "|" { PSP }
| "/" { SUBSTITUTION }
| "\\" { FUSION }
| "(" { LRB }
| ")" { RRB }
| "X" { CLOSURE }
| "@" { PERMUTATION }
| eof { EOF }
| _ { raise (Unknown_Symbol (lexbuf.Lexing.lex_curr_p.
Lexing.pos_lnum, (lexbuf.Lexing.lex_curr_p.
Lexing.pos_cnum-lexbuf.Lexing.lex_curr_p.Lexing.pos_bol))) }
Il primo pattern serve per scartare gli spazi mentre il secondo serve per chiamare la
funzione incr linenum per i motivi descritti in precedenza. I pattern successivi indivi-
duano e generano i token previsti dalla grammatica (Le stringhe num rappresentano
interi quindi e necessario fare un cast esplicito). L’ultimo pattern e il simbolo “-”
che corrisponde a qualsiasi stringa. Se l’analizzatore arriva a questo punto significa
che ha incontrato una stringa non valida e segnala la situazione alzando l’eccezione
Unknown Symbol.
4.3.1.4 Differenze per la generazione del programma OCaml
L’analizzatore per la variante del compilatore che genera il programma OCaml cor-
rispondente al bigrafo e praticamente identico a quello appena descritto. L’unica
differenza e che, dovendo generare stringhe, non e necessario fare il cast delle stringhe
num in interi.
4.3.2 Il Parser
Il parser e stato generato con il programma ocamlyacc. Tale programma prende un
file contenente la lista dei token, le loro proprieta e produzioni della grammatica e
produce il sorgente di un programma OCaml che implementa il parser. Il sorgente
4.3. ASPETTI IMPLEMENTATIVI 67
per ocamlyacc e diviso in quattro sezioni: intestazione, dichiarazioni, regole della
grammatica e blocco finale (quest’ultimo non e stato usato).
4.3.2.1 Intestazione
L’intestazione contiene codice OCaml che sara copiato all’inizio del sorgente del
parser. Questo blocco e racchiuso dai simboli “%{” e “%}”. Nell’intestazione di
solito si aprono i moduli e si dichiarano variabili, funzioni ed eccezioni. Questa e
l’intestazione del parser:
open String
open Algebra
open Bigraph
Per prima cosa si aprono questi tre moduli. Il primo contiene funzioni utili per
manipolare le stringhe, il secondo contiene le funzioni che implementano l’algebra
dei bigrafi diretti e l’ultimo contiene l’implementazione delle strutture dati per le
signatures e i bigrafi diretti
exception Unbound_Bigraph of (int*int)
exception Malformed_Dbpl of (int*int)
exception Malformed_Signature of (int*int)
exception Malformed_Signature_Element of (int*int)
exception Malformed_SiteSet of (int*int)
exception Malformed_SiteSet_Element of (int*int)
exception Malformed_IonLinkList of (int*int)
exception Malformed_IonLinkList_Element of (int*int)
exception Malformed_NameSet of (int*int)
exception Malformed_NameSet_Element of (int*int)
exception Malformed_Bigraph of (int*int)
exception Malformed_Merge of (int*int)
exception Malformed_Ion of (int*int)
exception Malformed_Substitution of (int*int)
exception Malformed_Fusion of (int*int)
exception Malformed_Permutation of (int*int)
exception Malformed_Tensor of (int*int)
exception Malformed_Osp of (int*int)
exception Malformed_Isp of (int*int)
68 CAPITOLO 4. IL LINGUAGGIO ED IL COMPILATORE
exception Malformed_Sp of (int*int)
exception Malformed_Posp of (int*int)
exception Malformed_Psp of (int*int)
exception Malformed_Composition of (int*int)
Queste sono le eccezioni sollevate in caso di errore. Ogni eccezione ha due parametri:
la riga e la colonna dove si e verificato l’errore
let ecount = ref (-1);;
let vcount = ref (-1);;
Questi sono i contatori per gli archi e i nodi; servono per dare i nomi ad essi dato
che il DBPL non prevede la possibilita di denominarli esplicitamente
let sign = new signatures;;
Dichiarazione di un oggetto di tipo signatures. Serve per gestire la signature letta
dal sorgente DBPL.
let bgcount = ref 0;;
let bigraphs = Hashtbl.create 16;;
let invbigraphs = Hashtbl.create 16;;
let ids = Hashtbl.create 16;;
Il primo e un contatore di bigrafi trovati, gli altri tre sono tabelle hash usate per
la gestione dei blocchi let. La prima associa un codice numerico ad un bigrafo; la
seconda associa un bigrafo ad un codice numerico e la terza associa un nome ad un
codice numerico
let rec perm str n = match str with
| [] -> []
| head::tail -> (head, n)::perm tail (n+1)
;;
let rec makeSigSet data = match data with
| [] -> sign
| (t, n, a)::tail -> sign#addsign2 n t a; makeSigSet tail
;;
let rec donames list c = match list with
| [] -> []
4.3. ASPETTI IMPLEMENTATIVI 69
| head::tail -> if (get head 0 = c)
then (sub head 1 (length head -1))::
donames tail c
else donames tail c
;;
let rec makelinklist l n = match l with
| [] -> []
| head::tail -> (n, (sub head 1 (length head -1)))::
makelinklist tail (n+1)
;;
let location pos = (pos.Lexing.pos_lnum,
pos.Lexing.pos_cnum-pos.Lexing.pos_bol);;
La prima funzione costruisce la lista delle coppie (sito, radice) da passare come
parametro alla funzione che crea il bigrafo permutation. La seconda inserisce gli
elementi della signature nella apposita classe. La terza filtra i link passati come
parametro a seconda della loro direzione (“+” per i link verso l’alto e “-” per i link
verso il basso). La quarta crea la lista che associa ad ogni porta di un nodo la
destinazione del link che parte da quella porta. Infine la quinta ritorna la posizione
di un token nella forma (riga, colonna)
4.3.2.2 Dichiarazioni
Questa sezione contiene le dichiarazioni dei simboli del linguaggio e le loro proprieta.
Ogni dichiarazione deve iniziare col simbolo “%”. Le dichiarazioni sono queste:
%token <int> NUM
%token <string> NAME
%token <string> SIGTYPE
%token <string> SIGN
%token EOF LET IN EQ
%token TENSOR COMPOSITION OSP ISP SP POSP PSP
%token SUBSTITUTION FUSION PERMUTATION
%token LRB RRB LSB RSB COLON SEMICOLON COMMA
%token MERGE CTRLLIST CLOSURE SIGNATURE
%left OSP ISP SP POSP PSP TENSOR
%left COMPOSITION
%nonassoc CLOSURE PERMUTATION
70 CAPITOLO 4. IL LINGUAGGIO ED IL COMPILATORE
%nonassoc SUBSTITUTION FUSION
%nonassoc MERGE
%start dbpl
%type <(Bigraph.signatures * Bigraph.directedbg)> dbpl
Le righe che iniziano con “token” introducono la definizione di un nuovo token.
Tra parentesi angolari c’e il tipo dell’eventuale valore associato al token. Le righe
che iniziano con left, right o nonassoc introducono la definizione di token che rap-
presentano una operazione. Permettono di specificare sia le precedenze (in ordine
crescende) sia l’associativita (left associa a sinistra, right associa a destra e nonassoc
non e associativo). La riga che inizia con start dichiara il simbolo iniziale e la riga
che inizia con type dichiara il suo tipo.
4.3.2.3 Regole
La sezione delle regole descrive tutte le produzioni della grammatica e le azioni da
eseguire quando si trova una corrispondenza tra i token incontrati e una produzione.
La sezione delle regole e separata da quella delle dichiarazioni da una coppia di
simboli percentuale (“%%”). La sintassi generica per una produzione e:
result:
symbol1 ... symboln {action}
| symbol1 ... symboln {action}
| ...
;
dove result e un simbolo non terminale, symbol1 . . . symboln sono simboli (terminali
o non) e action e il codice OCaml da eseguire per quella produzione. Si puo usare
l’espressione $n per accedere al valore associato all’n-esimo simbolo della produzione,
se presente. Nelle produzioni e possibile usare il simbolo terminale speciale “error”
per l’individuazione degli errori. Ogni volta che si incontra un nuovo token si cerca
una produzione per i token incontrati. Se la si trova, si applica l’azione prevista;
se non la si trova si legge il token successivo. Se si possono applicare due o piu
produzioni si applica la piu lunga. Se due o piu produzioni hanno la stessa lunghezza
allora la grammatica e ambigua. La prima produzione riguarda il simbolo iniziale:
dbpl: signature letblock EOF { ($1, $2) }
| error EOF {
4.3. ASPETTI IMPLEMENTATIVI 71
raise (Malformed_Dbpl (location (Parsing.rhs_start_pos 1))) }
| signature error EOF {
raise (Malformed_Dbpl (location (Parsing.rhs_start_pos 2))) }
;
un sorgente DBPL valido e formato da una signature e un bigrafo. Quasiasi cosa
diversa genera un errore (e il sollevamento della relativa eccezione).
letblock: bg { $1 }
| letelem IN letblock { $3 }
;
letelem: LET NAME EQ bg {
if (Hashtbl.mem invbigraphs $4)
then Hashtbl.replace ids $2 (Hashtbl.find invbigraphs $4)
else Hashtbl.add bigraphs !bgcount $4;
Hashtbl.replace ids $2 !bgcount;
Hashtbl.add invbigraphs $4 !bgcount;bgcount:=!bgcount+1 }
;
Queste due produzioni gestiscono i blocchi let. I blocchi let possono essere annidati a
piacere ma alla fine c’e sempre la definizione di un bigrafo. Nel caso dell’assegnazione
di un nome ad un bigrafo si controlla se tale bigrafo e gia stato incontrato. In caso
positivo tramite la tabella invbigraphs si ottiene il codice associato e si aggiunge nella
tabella ids l’associazione tra il nuovo nome e il codice cosı ottenuto. In caso negativo
si aggiunge alle tabelle bigraphs e invbigraphs le associazioni (codice, bigrafo) e
(bigrafo, codice) rispettivamente e si aggiunge alla tabella ids l’associazione (nome,
codice); infine si incrementa il contatore dei bigrafi
signature: SIGNATURE LSB RSB SEMICOLON { makeSigSet [] }
| SIGNATURE LSB sigelem RSB SEMICOLON { makeSigSet $3 }
| error {
raise (Malformed_Signature (location (Parsing.rhs_start_pos 1)))
}
;
sigelem: SIGTYPE NAME COLON NUM { [($1, $2, $4)] }
| sigelem COMMA SIGTYPE NAME COLON NUM { $1@[($3, $4, $6)]}
| error {raise(
Malformed_Signature_Element (location (Parsing.rhs_start_pos 1))
72 CAPITOLO 4. IL LINGUAGGIO ED IL COMPILATORE
)}
;
Queste sono le produzioni per gestire la signature in accordo con quanto visto in
4.2.2.1. Gli elementi della signature sono inseriti in una lista che e poi passata alla
funzione makeSigSet. In caso di errore di sintassi il partser solleva una eccezione
siteset: LSB RSB { [] }
| LSB siteelem RSB { $2 }
| error {raise (
Malformed_SiteSet (location (Parsing.rhs_start_pos 1))
)}
;
siteelem: NUM { [$1] }
| siteelem COMMA NUM { $1@[$3] }
| error {raise (
Malformed_SiteSet_Element (location (Parsing.rhs_start_pos 1))
)}
;
ctrllist: LSB RSB { [] }
| LSB ctrlelem RSB { $2 }
| error {raise (
Malformed_IonLinkList (location (Parsing.rhs_start_pos 1))
)}
;
ctrlelem: SIGN NAME { [($1^$2)] }
| ctrlelem COMMA SIGN NAME{ $1@[($3^$4)] }
| error {raise (
Malformed_IonLinkList_Element(location (Parsing.rhs_start_pos 1))
)}
;
nset: LSB nsetelem RSB { $2 }
| error {raise (
Malformed_NameSet (location (Parsing.rhs_start_pos 1))
)}
;
nsetelem: NAME COMMA NAME { $1::[$3] }
4.3. ASPETTI IMPLEMENTATIVI 73
| nsetelem COMMA NAME { $1@[$3] }
| error {raise (
Malformed_NameSet_Element (location (Parsing.rhs_start_pos 1))
)}
;
Queste produzioni riguardano le liste di siti, nomi e link per gli ioni in accordo
con quanto visto in 4.2.2.2. In caso di errore di sintassi il parser solleva l’eccezione
opportuna
bg: LRB bg RRB { $2 }
| merge { $1 }
| ion { $1 }
| substitution { $1 }
| fusion { $1 }
| closure { $1 }
| permutation { $1 }
| tensor { $1 }
| osp { $1 }
| isp { $1 }
| sp { $1 }
| posp { $1 }
| psp { $1 }
| composition { $1 }
| NAME { try (Hashtbl.find bigraphs(Hashtbl.find ids $1))
with Not_found ->
raise (Unbound_Bigraph (location (Parsing.rhs_start_pos 1))) }
;
Questa e la produzione che descrive tutti i possibili bigrafi. Un bigrafo puo essere un
bigrafo elementare, il risultato di una operazione tra bigrafi, un bigrafo tra parentesi
tonde (per forzare le precedenze tra operazioni) o un NAME (per usare bigrafi
denominati in precedenza). Gli errori sono gestiti dai singoli tipi di bigrafo tranne
che nell’ultimo caso. Se si tenta di usare un bigrafo non definito il parser solleva una
eccezione
merge: MERGE NUM { (merge $2) }
74 CAPITOLO 4. IL LINGUAGGIO ED IL COMPILATORE
| error NUM {
raise (Malformed_Merge (location (Parsing.rhs_start_pos 1))) }
| MERGE error {
raise (Malformed_Merge (location (Parsing.rhs_start_pos 2))) }
| MERGE NUM error {
raise (Malformed_Merge (location (Parsing.rhs_start_pos 3))) }
;
substitution: NAME SUBSTITUTION NAME { (substitution [$3] $1) }
| NAME SUBSTITUTION nset { (substitution $3 $1) }
| NAME SUBSTITUTION { (emptysubstitution [$1]) }
| nset SUBSTITUTION { (emptysubstitution $1) }
| nset SUBSTITUTION NAME {raise (
Malformed_Substitution (location (Parsing.rhs_start_pos 1))) }
| nset SUBSTITUTION nset { raise (
Malformed_Substitution (location (Parsing.rhs_start_pos 1))) }
;
fusion: NAME FUSION NAME { (fusion $3 [$1]) }
| nset FUSION NAME { (fusion $3 $1) }
| FUSION NAME { (emptyfusion [$2]) }
| FUSION nset { (emptyfusion $2) }
| NAME FUSION nset { raise (
Malformed_Fusion (location (Parsing.rhs_start_pos 3))) }
| nset FUSION nset { raise (
Malformed_Fusion (location (Parsing.rhs_start_pos 3))) }
;
permutation: PERMUTATION siteset { permutation (perm $2 0) }
Queste produzioni gestiscono la creazione dei bigrafi elementari merge, substitu-
tion, fusion e permutation in accordo con quanto visto in 4.2.2.2 usando le apposite
funzioni dell’algebra. In caso di errori di sintassi il parser alza le opportune eccezioni
closure: NAME CLOSURE NAME {
ecount:=!ecount+1; closure $3 $1 ("e" ^ string_of_int !ecount) }
| nset CLOSURE NAME {
ecount:=!ecount+1; (fusion "1unused" $1)#composition
(closure $3 "1unused" ("e" ^ string_of_int !ecount)) }
| NAME CLOSURE nset {
4.3. ASPETTI IMPLEMENTATIVI 75
ecount:=!ecount+1;
(closure "2unused" $1 ("e" ^ string_of_int !ecount))#composition
(substitution $3 "2unused") }
| nset CLOSURE nset { ecount:=!ecount+1;
((fusion "3unused" $1)#composition
(closure "4unused" "3unused"
("e" ^ string_of_int !ecount)))#composition
(substitution $3 "4unused") }
| NAME CLOSURE { ecount:=!ecount+1;
(upclosure $1 ("e" ^ string_of_int !ecount)) }
| CLOSURE NAME { ecount:=!ecount+1;
(downclosure $2 ("e" ^ string_of_int !ecount)) }
| nset CLOSURE { ecount:=!ecount+1;
(fusion "1unused" $1)#composition
(upclosure "1unused" ("e" ^ string_of_int !ecount)) }
| CLOSURE nset { ecount:=!ecount+1;
(downclosure "2unused" ("e" ^ string_of_int !ecount))#composition
(substitution $2 "2unused") }
;
Questa produzione gestisce la generazione del bigrafo closure (e anche up-closure
e down-closure) in accordo con quanto visto in 4.2.2.2 usando l’apposita funzione
dell’algebra. Le closures con nset si generano componento opportunamente bigrafi
substitution, bigrafi fusion e closures semplici
ion: NAME ctrllist { vcount:=!vcount+1;
try (let dbg = (discreteion ("v" ^ string_of_int !vcount) $1
(donames $2 ’-’ ) (donames $2 ’+’) (makelinklist $2 0) sign) in
if (sign#activity $1="atomic") then
(dbg#composition ((merge 0)#tensor (id 0 (donames $2 ’-’) [])))
else dbg
)with Not_found -> raise
(Malformed_Ion (location (Parsing.rhs_start_pos 1))) }
;
Questa produzione gestisce la generazione del bigrafo discrete ion in accordo con
quanto visto in 4.2.2.2 usando l’apposita funzione dell’algebra. Sono usate le fun-
76 CAPITOLO 4. IL LINGUAGGIO ED IL COMPILATORE
zioni donames e makelinklist per costruire i parametri da passare alla funzione di-
screteion. Se lo ione e atomico allora lo si compone con un bigrafo costruito ad-hoc
per rimuovere il sito
tensor: bg TENSOR bg { try ($1#tensor $3)
with Bigraphs_are_not_disjoint ->
raise (Malformed_Tensor (location (Parsing.rhs_start_pos 2)))}
;
osp: bg OSP bg { try ($1#osp $3)
with Bigraphs_are_not_disjoint ->
raise (Malformed_Osp (location (Parsing.rhs_start_pos 2)))}
;
isp: bg ISP bg { try ($1#isp $3)
with Bigraphs_are_not_disjoint ->
raise (Malformed_Isp (location (Parsing.rhs_start_pos 2)))}
;
sp: bg SP bg { try ($1#sp $3)
with Bigraphs_are_not_disjoint ->
raise (Malformed_Sp (location (Parsing.rhs_start_pos 2)))}
;
posp: bg POSP bg { try ($1#posp $3)
with Bigraphs_are_not_disjoint ->
raise (Malformed_Posp (location (Parsing.rhs_start_pos 2)))}
;
psp: bg PSP bg { try ($1#psp $3)
with Bigraphs_are_not_disjoint ->
raise (Malformed_Psp (location (Parsing.rhs_start_pos 2)))}
;
composition: bg COMPOSITION bg { try ($1#composition $3)
with Bigraphs_have_no_common_interface ->
raise (Malformed_Composition (location (Parsing.rhs_start_pos 2)))}
;
Queste produzioni gestiscono le operazioni tra bigrafi in accordo con quanto visto in
4.2.2.3 usando le operazioni della struttura dati che implementa i bigrafi diretti. In
caso di errori nell’esecuzione delle operazioni il parser solleva le eccezioni adeguate.
4.3. ASPETTI IMPLEMENTATIVI 77
4.3.2.4 Differenze per la generazione del programma ocaml
Anche in questo caso le differenze sono minime. La chiamata effettiva alla funzione
dell’algebra o della struttura dati che implementa i bigrafi diretti e sostituita con
una stringa con il testo della chiamata stessa. Nel programma ocaml generato c’e la
definizione di due funzioni senza parametri: compiledsignature e compiledbigraph.
Queste funzioni restituiscono rispettivamente la signature e il bigrafo descritti nel
sorgente dbpl.
4.3.3 L’interfaccia del compilatore
L’interfaccia del compilatore e formata da quattro funzioni che differiscono per i
parametri che prendono e il valore restituito.
let compilefromfile file =
let inChannel = open_in file in
let lexbuf = Lexing.from_channel inChannel in
Dbplparser.dbpl Dbpllexer.token lexbuf;;
Questa funzione legge il sorgente DBPL dal file specificato dal parametro e restituisce
la coppia (signature, bigrafo) generata dal parser.
let compilefromstring string =
let lexbuf = Lexing.from_string string in
Dbplparser.dbpl Dbpllexer.token lexbuf;;
Questa funzione e simile alla prima ma legge il sorgente dalla stringa passata come
parametro
let compilefromfile2ocaml filein fileout =
let inChannel = open_in filein in
let outChannel = open_out fileout in
let lexbuf = Lexing.from_channel inChannel in
Printf.fprintf outChannel "%s\n"
(Dbplparser2ocaml.dbpl Dbpllexer2ocaml.token lexbuf);;
Questa funzione legge il sorgente DBPL dal file specificato nel parametro e scrive il
risultato nel file specificato dal secondo parametro
78 CAPITOLO 4. IL LINGUAGGIO ED IL COMPILATORE
let compilefromstring2ocaml string fileout =
let outChannel = open_out fileout in
let lexbuf = Lexing.from_string string in
Printf.fprintf outChannel "%s\n"
(Dbplparser2ocaml.dbpl Dbpllexer2ocaml.token lexbuf);;
Infine questa funzione e simile alla precedente ma legge il sorgente dalla stringa
passata come parametro.
5Il decompilatore
In questo capitolo sara presentato il decompilatore. Esso svolge il ruolo inverso
del compilatore. Data una signature e un bigrafo, si occupa produrre una stringa
contenente codice DBPL. La compilazione di tale codice produrra una signature
ed un bigrafo uguali a quelli di partenza a meno dei nomi di nodi ed archi. Il
decompilatore e eseguito chiamando la sua funzione principale:
val printDBPL :
< getsigns : (string * (string * int)) list;
typeof : string -> int -> string; .. > ->
Bigraph.directedbg -> string
tale funzione ha due parametri: la classe delle signature e il bigrafo da decom-
pilare. La funzione chiama le funzioni printsignature e printBG per stampare le
componenti della signature e il bigrafo, rispettivamente.
5.1 Decompilazione della signature
La decompilazione della signature e molto semplice ed e implementata da questa
funzione:
val printsignature : (string * (string * int)) list -> string list
l’unico parametro della funzione e l’insieme degli elementi che compongono la
signature sotto forma di lista (tale lista e ottenuta chiamando il metodo getsigns
della classe signatures). Per ogni elemento la funzione costruisce una stringa secondo
la sintassi vista in 4.2.2.1. La funzione restituisce una lista di stringhe dove ogni
elemento della lista e un elemento della signature. Tale lista e convertita in una
stringa usando la funzione concat del modulo String (tale funzione si occupa anche
di inserire le virgole che separano gli elementi della signature).
80 CAPITOLO 5. IL DECOMPILATORE
5.2 Decompilazione del bigrafo
La decompilazione del bigrafo e implementata in questa funzione che prende come
parametri il bigrafo da decompilare e la classa delle signatures:
val printBG :
Bigraph.directedbg -> < typeof : string -> int -> string; .. >
-> string
per prima cosa si scompone il bigrafo passato come parametro nelle tre compo-
nenti che formano la forma normale. Successivamente la componente w e passata
alla funzione printW, mentre la componente d (composta con la componente w’ op-
portunamente modificata per semplificare il lavoro) e passata alla funzione printD.
Le funzioni printW e printD si occupano di produrre il codice DBPL che descrive il
bigrafo sotto forma di operazioni tra i bigrafi elementari visti nella sezione 4.2.2.2.
Una volta ottenute le stringhe che rappresentano questi bigrafi si controlla quali
componenti sono presenti e quali no per evitare di inserire simboli di composizione
non necessari.
5.2.1 Componente w
La funzione printW si occupa di produrre il codice DBPL che descrive la componente
W. Questa funzione prende come parametri la lista dei link e il numero di radici
presenti nella componente d
val printW : (Bigraph.tpoint * Bigraph.tlink) list -> int -> string
La funzione prima chiama linksdest per ottenere la lista delle coppie (destinazione
del link, tipo destinazione) e una tabella hash che associa ad ogni destinazione la
sorgente del link e il suo tipo. Questa lista e la tabella sono passate alla funzione
printW2 che per ogni destinazione di un link produce il codice che genera l’apposito
bigrafo elementare. La lista di stringhe con il codice DBPL e concatenata (se esiste)
con i simboli di tensore. Se la componente d ha delle radici, alla stringa appena
generata si aggiunge in coda un tensore con un bigrafo merge per permettere poi la
composizione della componente w con la componente d.
val printW2 :
(string * string) list -> (string, string * string) Hashtbl.t
-> string list
Questa e la funzione printW2. Essa prende come parametri la lista delle desti-
nazioni con il loro tipo e la tabella generata nella funzione printW. Questa funzione
5.2. DECOMPILAZIONE DEL BIGRAFO 81
si limita a chiamare per ogni destinazione di link nella lista la funzione printW3
passandole come parametro la destinazione in fase di elaborazione, il suo tipo e la
lista delle sorgenti dei link con quella destinazione (e il loro tipo).
val printW3 : string -> string -> (string * string) list -> string
Questa e la funzione che si occupa di generare il codice DBPL per i vari bigrafi
che formano la componente w. Il tipo della destinazione serve per determinare
quale bigrafo generare: se la destinazione e un arco allora bisogna generare un
bigrafo closure, se e un nome di interfaccia esterna verso l’alto bisogna generare un
bigrafo substitution, altrimenti bisogna generare un bigrafo fusion. Due chiamate a
namesoftype permettono di individuare i nomi che andranno a formare l’interfaccia
esterna e interna. Una volta ottenuti questi dati e possibile produrre il codice DBPL
opportuno.
5.2.2 Componente d
La funzione printD si occupa di produrre il codice DBPL relativo alla componente
D. Tale funzione prende come parametri il parent graph della componente, il link
graph della componente, la mappa di controllo, il numero di radici e la classe del-
le signatures. La funzione restituisce la stringa contenente il codice DBPL della
componente.
val printD :
Bigraph.tplace list ->
(Bigraph.tpoint * Bigraph.tlink) list ->
(string * ’a) list ->
int -> < typeof : string -> ’a -> string; .. > -> string
La funzione inizia creando la lista delle radici e due tabelle hash: la prima associa
ad ogni elemento padre del place graph la lista dei figli; la seconda associa ad ogni
nodo la porta, la destinazione e il tipo della destinazione di ogni link che parte da
quel nodo. Poi si chiama la funzione printroots che restituisce una lista di stringhe
dove ogni elemento e il codice DBPL che rappresenta una radice. Infine si chiama
passthroughlink per ottenere il codice DBPL per rappresentare i link che vanno da
un nome di interfaccia interna verso l’alto ad un nome di interfaccia esterna verso
l’alto e i link vanno da un nome di interfaccia esterna verso il basso ad un nome di
interfaccia interna verso il basso.
val printroots :
int list ->
82 CAPITOLO 5. IL DECOMPILATORE
(Bigraph.tprnt, Bigraph.tprnt) Hashtbl.t ->
(string, ’a * string * string) Hashtbl.t ->
(string * ’b) list -> < typeof : string -> ’b -> string; .. >
-> string list
Questa funzione scorre la lista delle radici e, per ogni radice della lista, produce il
codice DBPL che descrive tutti i figli della radice stessa. Per prima cosa si estrae la
lista dei figli della radice dalla tabella hash. Se la lista contiene almeno un elemento
si chiama la funzione printson che produce il codice relativo ai figli. Se invece la
radice non ha figli si genera semplicemente il codice per un bigrafo vuoto e si passa
alla radice successiva.
val printson :
Bigraph.tprnt list ->
(Bigraph.tprnt, Bigraph.tprnt) Hashtbl.t ->
(string, ’a * string * string) Hashtbl.t ->
(string * ’b) list ->
< typeof : string -> ’b -> string; .. > -> string list
-> string list
Questa funzione produce il codice DBPL relativo agli elementi presenti in una
lista passata come parametro. Se la lista e vuota allora produco il codice per generare
gli eventuali link verso il basso presenti. Se l’elemento attuale e un sito allora
costruisco la stringa che lo descrive aggiungendo una permutazione per aggiustare il
numero del sito. Se l’elemento attuale e un nodo allora procedo come segue:
1. determino l’arieta del nodo
2. cerco tutti i link che partono dal nodo e costruisco la lista ctrllist
3. se il nodo ha dei figli allora determino la lista di tutti i link verso l’alto di tutti
i figli
4. genero il codice per uno discrete ion
5. aggiungo l’eventuale codice per la lista dei links dei figli
6. compongo l’ion con il risultato della chiamata ricorsiva della funzione applicata
ai figli di questo nodo
7. se il nodo non ha figli genero semplicemente il codice per uno discrete ion
5.3. FUNZIONI AUSILIARIE 83
Se non rientro in uno dei casi precedenti scarto l’elemento e proseguo con la chiamata
ricorsiva
5.3 Funzioni ausiliarie
In questa stezione saranno descritte tutte le funzioni ausiliarie utilizzate dal decom-
pilatore
val filterlink : (’a * ’b * ’c) list -> ’c -> ’b list
Questa funzione prende come parametro una lista di triple (porta di partenza,
nome di destinazione, tipo) e un tipo di nome. Produce una lista di nomi destinazioni
di link il cui tipo e uguale al tipo passato come parametro
val sonslink :
Bigraph.tprnt ->
(string, ’a * ’b * string) Hashtbl.t ->
(Bigraph.tprnt, Bigraph.tprnt) Hashtbl.t -> ’b list
val sonslink1 :
Bigraph.tprnt list ->
(string, ’a * ’b * string) Hashtbl.t ->
(Bigraph.tprnt, Bigraph.tprnt) Hashtbl.t -> ’b list
Queste due funzione mutuamente ricorsive creano una lista di link verso nomi
di interfaccia esterna verso l’alto e che partono da tutti i figli di un certo elemento
passato come parametro.
val printionlinks : (’a * string * string) list -> string list
Questa funzione costruisce una ctrllist per un certo ion. Prende come parametro
una lista di triple (porta di partenza, nome di destinazione, tipo) e restituisce la
lista dei nomi di destinazione preceduti dai simboli “+” o “-” a seconda del tipo del
nome.
val printlinks : string list -> string -> string list
Questa funzione genera il codice di bigrafi substitution o fusion. Prende come
parametro una lista di nomi e una stringa che puo essere “+” o “-”. Per ogni
elemento della lista di nomi, genera il codice per un bigrafo substitution (se il secondo
parametro e “+”) o fusion (se il secondo parametro e “-”) usando l’elemento della
lista come nome sia d’interfaccia interna che esterna
val linkcompare : ’a * ’b * ’c -> ’a * ’d * ’e -> int
84 CAPITOLO 5. IL DECOMPILATORE
Questa funzione confronta due link in base al numero della porta di partenza. I
due link sono espressi sotto forma di triple (porta di partenza, nome di destinazione,
tipo). Tale funzione e usata per ordinare questo tipo di liste di link.
val linksdest :
(string, string * string) Hashtbl.t ->
(Bigraph.tpoint * Bigraph.tlink) list -> (string * string) list
Questa funzione crea una tabella hash che associa ad ogni nome di destinazione
il nome sorgente e il suo tipo. Restituisce una lista di coppie (destinazione, tipo
destinazione).
val namesoftype : (’a * ’b) list -> ’b -> ’a list
Questa funzione restituisce una lista di nomi sorgenti di link in base al loro tipo.
val hashprnt :
(Bigraph.tprnt, Bigraph.tprnt) Hashtbl.t ->
Bigraph.tplace list -> (Bigraph.tprnt, Bigraph.tprnt) Hashtbl.t
Questa funzione crea una tabella hash che associa ad ogni padre la lista dei suoi
figli.
val hashlink :
(string, int * string * string) Hashtbl.t ->
(Bigraph.tpoint * Bigraph.tlink) list ->
(string, int * string * string) Hashtbl.t
Questa funzione crea una tabella hash che associa ad ogni nodo la lista dei link
che partono da esso assieme alla porta di partenza e il tipo di link.
val findsites : Bigraph.tplace list -> int list
Questa funzione scorre il parent graph per trovare tutti i siti di un bigrafo.
val makebg : int list -> Bigraph.directedbg
val makebgaux : < addprntsiteroot : ’a -> ’a -> ’b; .. > ->
’a list -> unit
Queste due funzioni creano un bigrafo da usare per poter comporre la compo-
nente d con la componente w’.
val makerootlist : int -> int list -> int list
Questa funzione costruisce una lista di interi 0..n.
5.4. ESEMPO DI OUTPUT 85
y1
e3
e2
e0
e1
v4v5
v0v1
v2v3
Figura 5.1: Bigrafo d’esempio per il decompilatore
5.4 Esempo di output
In questa sezione sara mostrato l’output prodotto dal decompilatore dato un bigrafo.
Il bigrafo in figura 5.1 puo essere scritto in DBPL in questo modo:
Signature [active t4:1,atomic t5:1,atomic t2:2];
let a=merge 1|X[x,z]|(y1X[a,y]*t4 [-w])°(w\w*merge 1|t5 [+a]*y/y) in
let b=(((wX[b,c]*merge 1)°(b/b*t4 [+c])°t2 [+b,-d]|t2 [+x,-e])°([d,e]X)) in
let c = t2 [+z,+y] in
a°(b*c)
Per questo bigrafo il decompilatore produrra questo codice:
Signature [atomic t2:2,active t4:1,atomic t5:1];
(X[w3,w2]*X[w6,w4]*y1X[w7,w5]*X[w8,w1,w0]* merge 1)°
(((t4 [+w8]*w7/w7*w5/w5*w4/w4)°(t5 [+w7]|t2 [+w4,+w5])|
(t4 [+w1]*w3/w3*w0/w0)°(t2 [+w0,+w3])|t2 [+w6,+w2]))
6Il visualizzatore
In questo capitolo sara presentato il visualizzatore. Questo programma permette di
visualizzare un bigrafo qualsiasi sotto forma di immagine SVG. E stato scelto tale
formato principalmente per tre motivi:
• una immagine SVG e un file XML
• le immagini SVG sono immagini vettoriali. Questo significa che possono essere
ingrandite a piacere
• tali immagini possono essere visualizzate con un browser web che supporti tale
formato (nativamente o con un plug-in)
La generazione del codice svg e suddivisa in tre fasi: generazione del codice relativo
al place graph, posizionamento di archi e nomi e generazione del codice per i link. I
dati necessari per stampare il place graph sono inseriti in un albero di tipo datatree
cosı definito:
type datatree =
Elem of (Bigraph.tprnt * float * float *
float * float * datatree list);;
Il primo campo e l’elemento del place graph (puo essere una radice, un sito oppure
un nodo). I quattro successvi sono rispettivamente posizione orrizzontale, posi-
zione verticale, larghezza ed altezza dell’elemento e l’ultimo e una lista dei figli
dell’elemento.
6.1 Funzione principale
Il visualizzatore ha una funzione d’interfaccia principale: dbg2svg. Tale funzione
prende come parametro il bigrafo da disegnare e restituisce una stringa contenente
il codice SVG che rappresenta il bigrafo.
88 CAPITOLO 6. IL VISUALIZZATORE
val printBG :
Bigraph.directedbg -> string
La funzione opera in questo modo:
1. Estrae il place graph e il link graph dal bigrafo. Le informazioni del place
graph sono inserite in una tabella hash che associa ad ogni padre la lista dei
figli
2. Genera la lista delle radici
3. Crea l’albero “printpgraphgrezzo”. Tale albero e una rappresentazione del
place graph con l’aggiunta di dimensioni e posizioni di default degli elementi.
Se il bigrafo non contiene almeno una radice allora si inserisce una radice
fittizia per contenere gli eventuali link
4. Genera una lista di archi e calcola la loro dimensione totale in pixel. Questo
serve per determinare la minima spaziatura tra i nodi quando si dovra inserire
gli archi nell’immagine SVG
5. Calcola la lunghezza minima di una radice in base alle dimensioni richieste per
inserire tutti i nomi di interfaccia esterna ed interna relativi a quella radice.
Un nome sara disegnato sopra o sotto una radice se c’e almeno un link che
arriva a quel nome partendo da uno dei figli di quella radice. I link che non
partono da nodi sono considerati relativi alla prima radice
6. Calcola le dimensioni di tutti gli elementi che compongono il place graph. Tali
informazioni sono inserite nell’albero generato in precedenza
7. Imposta la stessa altezza in pixel per tutte le radici
8. Determina la posizione assoluta degli elementi del place graph in base alle loro
dimensioni
9. Determina la posizione di nomi ed archi
10. Chiama le funzioni che si occupano di generare il codice SVG per tutti gli
elementi del bigrafo usando i dati calcolati in precedenza
6.2 Codice per il place graph
La generazione del codice per il place graph e eseguita da due funzioni mutuamente
ricorsive. La prima si occupa di chiamare la seconda su ogni elemento di una lista
6.3. POSIZIONI DI ARCHI E NOMI 89
alberi di tipo datatree mentre la seconda genera il codice per quell’elemento e chiama
la prima sui figli dell’elemento.
val printpgraph : datatree list -> string
val printpgraph2 : datatree -> string
Le funzioni sono molto semplici. In base all’elemento attuale si decide che figura
SVG generare: rettangoli per radici e siti, ellissi per i nodi. La seconda funzione si
occupa anche di inserire sotto forma di testo i numeri che indentificano radici e siti
e le stringhe che identificano i nodi.
6.3 Posizioni di archi e nomi
Questa fase ha come scopo deteminare le coordinate dove disegnare tutti gli archi
e nomi ed usa principalmente tre funzioni: rootlinkslength, placelinks e placeedges.
La prima funzione decide in quali radici andranno disegnati i nomi, la lunghezza
minima di ogni radice per contenere i nomi necessari e prepara una lista che conterra
le coordinate dove disegnare ogni nome.
val rootlinkslength :
datatree list ->
(Bigraph.tpoint * Bigraph.tlink) list ->
Bigraph.StringSet.t ->
Bigraph.StringSet.t ->
Bigraph.StringSet.t ->
Bigraph.StringSet.t -> int -> (int * float * float) list
Questa funzione prende come parametri l’algebero grezzo, la lista dei link, le
liste dei quattro tipi di nomi. L’ultimo parametro serve per gestire l’inserimento dei
link che non partono da nodi nella prima radice. La funzione restituisce una lista
che associa ad ogni radice la larghezza minima che deve avere per contenere i nomi
dell’interfaccia estera ed interna. Inoltre inizializza una lista globale che associa
ad ogni nome la radice di cui fara parte, le coordinate di default e il suo tipo. La
funzione inizia determinando tutti i suoi nodi figli (con la funzione allnodes) e i nomi
destinazioni di link che partono dai nodi appena trovati (con la funzione rootlinks)
inserendo i dati necessari nella lista globale. Successivamente determina i nomi di
interfaccia esterna verso l’alto e di interfaccia interna verso il basso che non partono
dai nodi. Tali nomi sono inseriti nella lista globale. Infine con i dati raccolti si
calcola la lunghezza totale delle due interfaccie della radice (i nomi di interfaccia
90 CAPITOLO 6. IL VISUALIZZATORE
esterna verso l’alto e di interfaccia interna verso il basso che non partono dai nodi
contribuiscono a questo calcolo solo per la prima radice).
La funzione placelinks si occupa di completare i dati ottenuti dalla lista globale
con le coordinate dei nomi.
val placelinks :
datatree list ->
(string * (int * ’a * ’b * string)) list ->
(string * (float * float * string)) list
val placelinks2 :
(string * (’a * ’b * ’c * string)) list ->
float -> float -> float -> (string * (float * float * string)) list
val allnamescmp :
’a * (’b * ’c * ’d * string) -> ’e * (’f * ’g * ’h * string) -> int
Placelinks prende come parametri l’albero e la lista globale e usa placelinks2 per
calcolare le coordinate dei nomi di ogni radice. I nomi sono ordinati (usando allna-
mescmp per i confronti) in modo che siano considerati prima i nomi di interfaccia
esterna verso il basso e i nomi di interfaccia interna verso l’alto. Placelinks2 imposta
le coordinate di ogni nome in base all’interfaccia di appartenenza, la lunghezza del
nome e una spaziatura minima tra ogni nome
Infine placeedges si occupa di determinare le coordinate per la stampa di ogni
arco. Questa funzione prende come parametri la lista degli archi e l’albero con la
prima radice e restituisce una coppia dove il primo elemento e una lista che associa
ad ogni arco le sue coortinate e la seconda e il codice SVG che disegna l’arco.
La funzione disegna gli archi nella prima radice tra il primo e il secondo nodo, se
presenti. La funzione placeedges2 si occupa di generare concretamente la lista di
associazione e il codice SVG.
val printedges :
string list ->
datatree -> float -> ((string * (float * float)) * string) list
val printedges2 :
string list -> float -> float
-> ((string * (float * float)) * string) list
6.4. CODICE PER I LINK 91
6.4 Codice per i link
La generazione del codice SVG per i link e gestita dalla funzione printlink che prende
come parametri l’albero con i dati del place graph, la lista di tutti i link del bigrafo,
due liste con i dati sulle posizioni di nomi ed archi e l’altezza in pixel delle radici.
val printlink :
datatree list ->
(Bigraph.tpoint * Bigraph.tlink) list ->
(string * (float * float * string)) list ->
(string * (float * float)) list -> float -> string
Molto semplicemente la funzione costruisce una tabella ausiliaria con i dati sulle
coordinate di partenza dei link che partono da nodi (usando le funzioni countocc e ha-
shtree, descritte nella sezione 6.5) e chiama printlink2 che si occupa della generazione
del codice SVG a partire da questi dati.
La funzione printlink2 prende come parametri la lista di tutti i link, una tabella
con le informazioni sulle coordinate di partenza dei link che partono da nodi, i dati
sulle posizioni di nomi e archi e l’altezza delle radici. Il suo codice e questo:
val printlink2 :
(Bigraph.tpoint * Bigraph.tlink) list ->
(string,
float * float * float * float * float * float * float * float *
float * float * float)
Hashtbl.t ->
(string * (float * float * string)) list ->
(string * (float * float)) list -> float -> string
La funzione detemina il codice da generare in base alla sorgente e alla destina-
zione dei link. Per i link che partono da nodi prima si determinano le coordinate di
partenza estraendole dalla tabella hash passata come parametro. Poi si deteminano
le coordinate di destinazione estraendole dalla liste passate come parametro a secon-
da della destinazione del link (nms per i nomi, edg per gli archi). Poi si aggiorna la
tabella hash inserendo il nuovo punto di partenza per il successivo link dello stesso
tipo e infine si genera il codice SVG usando i dati appena estratti e aggiungendo
punti intermedi per evitare le sovrapposizioni. Per i link che partono di nomi basta
estrarre le coordinate di partenza e destinazione dalle liste passate come parametro
e generare il codice SVG come appena visto per i nodi. Alla fine di ogni caso c’e la
92 CAPITOLO 6. IL VISUALIZZATORE
chiamata ricorsiva per generare il codice del link successivo.
6.5 Altre funzioni
In questa sezione saranno descritte tutte le altre funzioni usate dal visualizzatore
val hashprnt :
(Bigraph.tprnt, Bigraph.tprnt) Hashtbl.t ->
Bigraph.tplace list -> (Bigraph.tprnt, Bigraph.tprnt) Hashtbl.t
Questa funzione crea una tabella hash che associa ad ogni elemento padre la lista
dei figli
val filltree :
Bigraph.tprnt list ->
(Bigraph.tprnt, Bigraph.tprnt) Hashtbl.t -> datatree list
val filltree2 :
Bigraph.tprnt -> (Bigraph.tprnt, Bigraph.tprnt) Hashtbl.t
-> datatree
Queste due funzioni creano l’albero grezzo che contiene solo il place graph e le
dimensioni di default degli elementi. filltree si occupa di chiamare filltree2 per ogni
elemento padre del place graph. filltree2 costruisce l’albero grezzo con le dimensioni
di default degli elementi e chiama ricorsivamente filltree per costruire l’albero con i
dati dei figli
val fixsize : datatree list -> (int * float) list -> float
-> datatree list
val fixsize2 : datatree -> (int * float) list -> float -> datatree
val xsize :
datatree list ->
(int * float) list -> float -> float * datatree list
val xsize1 : datatree list -> float
Queste funzioni si occupano di calcolare le dimensioni minime di ogni elemento
dell’albero in funzione delle dimensioni dei figli. fixsize si occupa di chiamare fixsize2
su ogni elemento della lista di datatree passata come parametro. fixsize2 calcola la
dimensione di ogni elemento in base alle dimensioni dei figli. La funzione xsize e
usata per ottenere questi dati. xsize restituisce una coppia formata dalla larghezza
totale dei figli e dal datatree dei figli con le dimensioni gia calcolate. Per ottenere il
6.5. ALTRE FUNZIONI 93
datatree dei figli xsize chiama ricorsivamente fixsize. Per ottenere la larghezza totale
dei figli xsize chiama xsize1 la quale si limita a sommare le larghezze degli elementi
presenti in una lista di datatree.
val heights : datatree list -> float list
Questa funzione crea una lista con le altezze di tutte le radici.
val fixheight : datatree list -> datatree list
Questa funzione imposta l’altezza di una radice in modo tale che ci sia una
spaziatura di 60 pixel sopra e sotto il suo nodo figlio piu alto. Per fare cio scorre la
lista dei datatree passata come parametro e per ogni radice imposta la sua altezza
a 120 pixel piu l’altezza del figlio piu alto.
val setrootsheight : datatree list -> float -> datatree list
Questa funzione imposta l’altezza delle radici ad un valore passato come para-
metro. E usata per uniformare l’altezza di tutte le radici.
val fixpos : datatree list -> float -> float -> float
-> datatree list
val fixpos2 : datatree -> float -> float -> float -> float
-> datatree
val fixpos3 :
datatree list ->
float -> float -> float -> float -> float -> datatree list
Queste tre funzioni si occupano di calcolare la posizione assoluta di ogni elemento
del datatree in base alle sue dimensioni. fixpos chiama fixpos2 su ogni elemento
della lista di datatree passata come parametro e si assicura che tutte le radici siano
distanziate orrizzontalmente di 20 pixel. fixpos2 e fixpos3 si chiamano mutuamente
per calcolare le posizioni dei figli di una radice. I figli di una radice sono distanziati
di 60 pixel. I figli dei figli (e tutto il resto della discendenza) sono distanziati di 30
pixel.
val allnodes : datatree list -> string list
Questa funzione crea una lista di nomi di nodi scandendo l’albero passato come
parametro.
val filterlinksfromport :
(Bigraph.tpoint * Bigraph.tlink) list -> string list -> string
-> string list
94 CAPITOLO 6. IL VISUALIZZATORE
Questa funzione crea una lista di destinazioni di link che partono da nodi che
appartendono ad una certa radice e sono del tipo passato come parametro.
val nonportlinks :
(Bigraph.tpoint * Bigraph.tlink) list -> string
-> (string * string) list
Questa funzione crea una lista di coppie (destinazione, tipo) che contiene solo
destinazioni di link di un certo tipo e che partono da nomi.
val eqtn : float -> float -> float -> float -> float -> float
-> float
Questa funzione calcola la coordinata y di un punto che appartiene ad una ellisse
dati i suoi parametri. E usata per determinare il punto sorgente di un link durante
la stampa del codice SVG dei link.
val thisrootlinks :
’a -> (’b * (’a * ’c * ’d * ’e)) list
-> (’b * (’a * ’c * ’d * ’e)) list
Questa funzione individua tutti i link che partono dai figli di una radice passata
come parametro. I link sono espressi con una lista che associa il nome di destinazione
alla radice a cui appartiene, alle sue coordinate (del nome) e il tipo del link.
val printnames : (string * (float * float * ’a)) list -> string
Questa funzione genera il codice SVG per disegnare un nome. I dati sono forniti
da una lista che associa ad ogni nome le coordinate dove disegnarlo.
val findinlist :
’a -> (’a * (float * float * ’b)) list -> ’b -> float * float
Questa funzione, dato un nome, trova le sue coordinate.
val countocc :
(string, float * float * float) Hashtbl.t ->
(Bigraph.tpoint * Bigraph.tlink) list ->
(string, float * float * float) Hashtbl.t
Questa funzione conta quanti link partono da un nome e vanno in un nome
d’interfaccia esterna verso l’alto, in un nome di interfaccia interna verso il basso e
in un arco.
val hashtree :
datatree list ->
(string,
6.5. ALTRE FUNZIONI 95
float * float * float * float * float * float * float * float *
float * float * float)
Hashtbl.t -> (string, float * float * float) Hashtbl.t -> unit
Questa funzione calcola la spaziatura minima necessaria per disegnare tutti i link
che partono da un nodo.
val edges : (Bigraph.tpoint * Bigraph.tlink) list -> string list
Questa funzione genera una lista con tutti gli archi del bigrafo.
val rootlinks :
string list ->
(Bigraph.tpoint * Bigraph.tlink) list -> string list -> string
-> string list
Questa funzione trova tutti i nomi che sono destinazioni di link che partono dai
nodi figli di una data radice.
val makelist : ’a list -> ’b -> ’c
-> (’a * (’b * float * float * ’c)) list
Questa funzione crea una lista grezza che associa ad ogni nome la radice in cui
si trova, le coordinate di default e il suo tipo.
val in_allnodes_list : ’a -> (’a * (’b * ’c * ’d * ’e)) list -> bool
Questa funzione determina se un nome appariene ad una lista di nomi.
val singleallnodes :
(’a * (’b * ’c * ’d * ’e)) list -> (’a * (’b * ’c * ’d * ’e)) list
Questa funzione elimina i duplicati da una lisa di nomi.
val linksfromname :
(Bigraph.tpoint * Bigraph.tlink) list -> string -> string list
Questa funzione trova i nomi destinazioni di link che partono da un nome e sono
di un dato tipo.
val difference : ’a list -> ’a list -> ’a list
Questa funzione i nomi destinazione di link che non sono gia destinazioni di link
che partono da un nodo.
val maxvalue : (’a * ’b * ’b) list -> (’a * ’b) list
Questa funzione calcola la larghezza minima di una radice in base alla lunghezza
dei nomi che compongono le due interfaccie del bigrafo.
val makerootlist : int -> int -> Bigraph.tprnt list
Questa funzione crea una lista [0..n].
96 CAPITOLO 6. IL VISUALIZZATORE
Figura 6.1: Bigrafo d’esempio in SVG
6.6 Esempio di output
Avendo a disposizione il compilatore DBPL e il visualizzatore e possibile combinarli
per creare un programma che, dato bigrafo in DBPL, produca la corrispondente
immagine SVG. Una funzione che implementa questo programma e:
let drawbg =
let out = open_out (Sys.argv.(2)) in
let bg = (snd(compilefromfile (Sys.argv.(1)))) in
Printf.fprintf out "%s" (dbg2svg bg);;
Dato questo codice DBPL:
Signature [active t4:1,atomic t5:1,atomic t2:2];
let a=merge 1|X[x,z]|(y1X[a,y]*t4 [-w])°(w\w*merge 1|t5 [+a]*y/y) in
let b=(((wX[b,c]*merge 1)°(b/b*t4 [+c])°t2 [+b,-d]|t2 [+x,-e])°([d,e]X)) in
let c = t2 [+z,+y] in
a°(b*c)
il bigrafo corrispondente e mostrato in figura 6.1
7Conclusioni
In questo lavoro e stata presentata una implementazione degli strumenti per utiliz-
zare in modo semplice il meta-modello dei bigrafi diretti; in particolare e stato pro-
gettato un linguaggio per la descrizione dei bigrafi diretti e sono stati implementati
il compilatore, il decompilatore e il visualizzatore.
Il linguaggo per bigrafi diretti, chiamato DBPL, e un linguaggio simile ad un
linguaggio di programmazione funzionale e consente di descrivere la signature di
controllo e bigrafi diretti qualsiasi ottenuti dai bigrafi elementari e dalle operazioni
su di essi. Il linguaggio consente anche la denominazione dei bigrafi per riusarli in
seguito.
Il compilatore implementa il linguaggio per i bigrafi ed e formato da un ana-
lizzatore lessicale, un parser e un insieme di funzioni d’interfaccia. Il compilatore
puo generare gli oggetti OCaml corrispondenti alla signature e al bigrafo oppure un
programma OCaml. In tale programma sono definite due funzioni che restituiscono
la signature e il bigrafo descritti nel sorgente compilato.
Il decompilatore svolge il ruolo inverso del compilatore. Dato un bigrafo e una
signature, il decompilatore genera il relativo sorgente DBPL.
Infine il visualizzatore e in grado di produrre una immagine SVG corrispondente
ad un dato bigrafo. L’immagine e manipolabile con i programmi che supportano
tale formato ed e visualizzabile con un semplice browser web.
I lavori futuri si dividono in due parti: completamento delle funzioni di libreria
e completamento degli strumenti di sviluppo.
La prima parte prevede la realizzazione delle funzioni che costruiscono gli IPO e
che rendono possibile il matching dei bigrafi. Queste funzioni sono complementari
agli RPO per la costruzione degli LTS. Il matching dei bigrafi e usato per le rea-
zioni. Trovando combinazioni note di elementi in un bigrafo, se esiste una regola di
reazione, esse possono essere riscritte come il risultato di queste reazioni. Una vol-
98 CAPITOLO 7. CONCLUSIONI
ta implementato il matching sara possibile scrivere dei simulatori. Questo lavoro e
stato fatto da Birkedal, Damgaard, Glenstrup, Milner in [1] per i bigrafi non diretti.
La seconda parte prevede il completamento degli strumenti per usare il bigrafi
diretti, in particolare l’interfaccia grafica. L’interfaccia permettera agli utenti di
disegnare facilmente un bigrafo e di esportarlo sotto forma di sorgente DBPL. Per
l’implementazione di tale interfaccia si e pensato di usare il linguaggio Java e i
framework JGraph e JGraphpad [aggiungere riferimento], framework che servono
per realizzare editor di grafi generici.
L’obiettivo e realizzare una interfaccia simile all’interfaccia BPLweb per i bigrafi
non diretti disponibile su http://tiger.itu.dk:8080/bplweb/.
ASorgenti del compilatore
A.1 Generazione delle coppie (signature,bigrafo)
A.1.1 Analizzatore lessicale
{
open Dbplparser
exception Unknown_Symbol of (int*int);;
let incr_linenum lexbuf =
let pos = lexbuf.Lexing.lex_curr_p in
lexbuf.Lexing.lex_curr_p <- { pos with
Lexing.pos_lnum = pos.Lexing.pos_lnum + 1;
Lexing.pos_bol = pos.Lexing.pos_cnum;
}
;;
}
let num = [’0’-’9’] | [’1’-’9’][’0’-’9’]+
let name = [’a’-’z’][’a’-’z’ ’A’-’W’ ’Y’-’Z’ ’0’-’9’]*
let sign = ’+’ | ’-’
rule token = parse
| [’ ’ ’\t’] { token lexbuf }
| ’\n’ { incr_linenum lexbuf; token lexbuf }
| "merge" { MERGE }
| "Signature" { SIGNATURE }
| "active"
| "passive"
| "atomic" as sigtype { SIGTYPE (sigtype) }
100 APPENDICE A. SORGENTI DEL COMPILATORE
| "in" { IN }
| "let" { LET }
| num as n { NUM(int_of_string n) }
| name as nm { NAME(nm) }
| sign as sn { SIGN (String.make 1 sn) }
| "=" { EQ }
| "[" { LSB }
| "]" { RSB }
| "," { COMMA }
| ":" { COLON }
| ";" { SEMICOLON }
| "*" { TENSOR }
| "°" { COMPOSITION }
| "/\\" { OSP }
| "\\/" { ISP }
| "||" { SP }
| "/^\\" { POSP }
| "|" { PSP }
| "/" { SUBSTITUTION }
| "\\" { FUSION }
| "(" { LRB }
| ")" { RRB }
| "X" { CLOSURE }
| "@" { PERMUTATION }
| eof { EOF }
| _ { raise (Unknown_Symbol
(lexbuf.Lexing.lex_curr_p.Lexing.pos_lnum,
(lexbuf.Lexing.lex_curr_p.Lexing.pos_cnum-
lexbuf.Lexing.lex_curr_p.Lexing.pos_bol))) }
A.1.2 Parser
%{
open String
open Algebra
open Bigraph
A.1. GENERAZIONE DELLE COPPIE (SIGNATURE,BIGRAFO) 101
exception Unbound_Bigraph of (int*int)
exception Malformed_Dbpl of (int*int)
exception Malformed_Signature of (int*int)
exception Malformed_Signature_Element of (int*int)
exception Malformed_SiteSet of (int*int)
exception Malformed_SiteSet_Element of (int*int)
exception Malformed_IonLinkList of (int*int)
exception Malformed_IonLinkList_Element of (int*int)
exception Malformed_NameSet of (int*int)
exception Malformed_NameSet_Element of (int*int)
exception Malformed_Bigraph of (int*int)
exception Malformed_Merge of (int*int)
exception Malformed_Ion of (int*int)
exception Malformed_Substitution of (int*int)
exception Malformed_Fusion of (int*int)
exception Malformed_Permutation of (int*int)
exception Malformed_Tensor of (int*int)
exception Malformed_Osp of (int*int)
exception Malformed_Isp of (int*int)
exception Malformed_Sp of (int*int)
exception Malformed_Posp of (int*int)
exception Malformed_Psp of (int*int)
exception Malformed_Composition of (int*int)
let ecount = ref (-1);;
let vcount = ref (-1);;
let sign = new signatures;;
let bigraphs = Hashtbl.create 16;;
let invbigraphs = Hashtbl.create 16;;
let bgcount = ref 0;;
let ids = Hashtbl.create 16;;
let rec perm str n = match str with
| [] -> []
| head::tail -> (head, n)::perm tail (n+1)
;;
let rec makeSigSet data = match data with
| [] -> sign
102 APPENDICE A. SORGENTI DEL COMPILATORE
| (t, n, a)::tail -> sign#addsign2 n t a; makeSigSet tail
;;
let rec donames list c = match list with
| [] -> []
| head::tail -> if (get head 0 = c)
then (sub head 1 (length head -1))::donames tail c
else donames tail c
;;
let rec makelinklist l n = match l with
| [] -> []
| head::tail ->
(n, (sub head 1 (length head -1)))::makelinklist tail (n+1)
;;
let location pos = (pos.Lexing.pos_lnum,
pos.Lexing.pos_cnum-pos.Lexing.pos_bol);;
%}
%token <int> NUM
%token <string> NAME
%token <string> SIGTYPE
%token <string> SIGN
%token EOF LET IN EQ
%token TENSOR COMPOSITION OSP ISP SP POSP PSP
%token SUBSTITUTION FUSION PERMUTATION
%token LRB RRB LSB RSB COLON SEMICOLON COMMA
%token MERGE CTRLLIST CLOSURE SIGNATURE
%left OSP ISP SP POSP PSP TENSOR
%left COMPOSITION
%nonassoc CLOSURE PERMUTATION
%nonassoc SUBSTITUTION FUSION
%nonassoc MERGE
%start dbpl
%type <(Bigraph.signatures * Bigraph.directedbg)> dbpl
%%
dbpl: signature letblock EOF { ($1, $2) }
| error EOF {
raise (Malformed_Dbpl (location (Parsing.rhs_start_pos 1))) }
A.1. GENERAZIONE DELLE COPPIE (SIGNATURE,BIGRAFO) 103
| signature error EOF {
raise (Malformed_Dbpl (location (Parsing.rhs_start_pos 2))) }
;
letblock: bg { $1 }
| letelem IN letblock { $3 }
;
letelem: LET NAME EQ bg {
if (Hashtbl.mem invbigraphs $4)
then Hashtbl.replace ids $2 (Hashtbl.find invbigraphs $4)
else Hashtbl.add bigraphs !bgcount $4;
Hashtbl.replace ids $2 !bgcount;
Hashtbl.add invbigraphs $4 !bgcount;
bgcount:=!bgcount+1 }
;
signature: SIGNATURE LSB RSB SEMICOLON { makeSigSet [] }
| SIGNATURE LSB sigelem RSB SEMICOLON { makeSigSet $3 }
| error {
raise (Malformed_Signature (location (Parsing.rhs_start_pos 1))) }
;
sigelem: SIGTYPE NAME COLON NUM { [($1, $2, $4)] }
| sigelem COMMA SIGTYPE NAME COLON NUM { $1@[($3, $4, $6)]}
| error {
raise (Malformed_Signature_Element (location (Parsing.rhs_start_pos 1))) }
;
siteset: LSB RSB { [] }
| LSB siteelem RSB { $2 }
| error {
raise (Malformed_SiteSet (location (Parsing.rhs_start_pos 1))) }
;
siteelem: NUM { [$1] }
| siteelem COMMA NUM { $1@[$3] }
| error {
raise (Malformed_SiteSet_Element (location (Parsing.rhs_start_pos 1))) }
;
ctrllist: LSB RSB { [] }
| LSB ctrlelem RSB { $2 }
104 APPENDICE A. SORGENTI DEL COMPILATORE
| error {
raise (Malformed_IonLinkList (location (Parsing.rhs_start_pos 1))) }
;
ctrlelem: SIGN NAME { [($1^$2)] }
| ctrlelem COMMA SIGN NAME{ $1@[($3^$4)] }
| error {
raise (Malformed_IonLinkList_Element (location (Parsing.rhs_start_pos 1))) }
;
nset: LSB nsetelem RSB { $2 }
| error {
raise (Malformed_NameSet (location (Parsing.rhs_start_pos 1))) }
;
nsetelem: NAME COMMA NAME { $1::[$3] }
| nsetelem COMMA NAME { $1@[$3] }
| error {
raise (Malformed_NameSet_Element (location (Parsing.rhs_start_pos 1))) }
;
bg: LRB bg RRB { $2 }
| merge { $1 }
| ion { $1 }
| substitution { $1 }
| fusion { $1 }
| closure { $1 }
| permutation { $1 }
| tensor { $1 }
| osp { $1 }
| isp { $1 }
| sp { $1 }
| posp { $1 }
| psp { $1 }
| composition { $1 }
| NAME {
try (Hashtbl.find bigraphs(Hashtbl.find ids $1))
with Not_found ->
raise (Unbound_Bigraph (location (Parsing.rhs_start_pos 1))) }
;
A.1. GENERAZIONE DELLE COPPIE (SIGNATURE,BIGRAFO) 105
merge: MERGE NUM { (merge $2) }
| error NUM {
raise (Malformed_Merge (location (Parsing.rhs_start_pos 1))) }
| MERGE error {
raise (Malformed_Merge (location (Parsing.rhs_start_pos 2))) }
| MERGE NUM error {
raise (Malformed_Merge (location (Parsing.rhs_start_pos 3))) }
;
ion: NAME ctrllist {
vcount:=!vcount+1;
try (let dbg = (discreteion ("v" ^ string_of_int !vcount) $1
(donames $2 ’-’ ) (donames $2 ’+’)
(makelinklist $2 0) sign) in
if (sign#activity $1="atomic")
then (dbg#composition ((merge 0)#tensor (id 0 (donames $2 ’-’) [])))
else dbg)
with Not_found -> raise (Malformed_Ion (location (Parsing.rhs_start_pos 1))) }
;
substitution: NAME SUBSTITUTION NAME { (substitution [$3] $1) }
| NAME SUBSTITUTION nset { (substitution $3 $1) }
| NAME SUBSTITUTION { (emptysubstitution [$1]) }
| nset SUBSTITUTION { (emptysubstitution $1) }
| nset SUBSTITUTION NAME {
raise (Malformed_Substitution (location (Parsing.rhs_start_pos 1))) }
| nset SUBSTITUTION nset {
raise (Malformed_Substitution (location (Parsing.rhs_start_pos 1))) }
;
fusion: NAME FUSION NAME { (fusion $3 [$1]) }
| nset FUSION NAME { (fusion $3 $1) }
| FUSION NAME { (emptyfusion [$2]) }
| FUSION nset { (emptyfusion $2) }
| NAME FUSION nset {
raise (Malformed_Fusion (location (Parsing.rhs_start_pos 3))) }
| nset FUSION nset {
raise (Malformed_Fusion (location (Parsing.rhs_start_pos 3))) }
;
106 APPENDICE A. SORGENTI DEL COMPILATORE
closure: NAME CLOSURE NAME {
ecount:=!ecount+1;
closure $3 $1 ("e" ^ string_of_int !ecount) }
| nset CLOSURE NAME {
ecount:=!ecount+1;
(fusion "1unused" $1)#composition
(closure $3 "1unused" ("e" ^ string_of_int !ecount)) }
| NAME CLOSURE nset {
ecount:=!ecount+1;
(closure "2unused" $1 ("e" ^ string_of_int !ecount))#composition
(substitution $3 "2unused") }
| nset CLOSURE nset {
ecount:=!ecount+1;
((fusion "3unused" $1)#composition
(closure "4unused" "3unused" ("e" ^ string_of_int !ecount)))#composition
(substitution $3 "4unused") }
| NAME CLOSURE {
ecount:=!ecount+1;
(upclosure $1 ("e" ^ string_of_int !ecount)) }
| CLOSURE NAME {
ecount:=!ecount+1;
(downclosure $2 ("e" ^ string_of_int !ecount)) }
| nset CLOSURE {
ecount:=!ecount+1;
(fusion "1unused" $1)#composition
(upclosure "1unused" ("e" ^ string_of_int !ecount)) }
| CLOSURE nset {
ecount:=!ecount+1;
(downclosure "2unused" ("e" ^ string_of_int !ecount))#composition
(substitution $2 "2unused") }
;
permutation: PERMUTATION siteset { permutation (perm $2 0) }
;
tensor: bg TENSOR bg {
try ($1#tensor $3)
with Bigraphs_are_not_disjoint ->
A.2. GENERAZIONE DEL PROGRAMMA OCAML 107
raise (Malformed_Tensor (location (Parsing.rhs_start_pos 2)))}
;
osp: bg OSP bg {
try ($1#osp $3)
with Bigraphs_are_not_disjoint ->
raise (Malformed_Osp (location (Parsing.rhs_start_pos 2)))}
;
isp: bg ISP bg {
try ($1#isp $3)
with Bigraphs_are_not_disjoint ->
raise (Malformed_Isp (location (Parsing.rhs_start_pos 2)))}
;
sp: bg SP bg {
try ($1#sp $3)
with Bigraphs_are_not_disjoint ->
raise (Malformed_Sp (location (Parsing.rhs_start_pos 2)))}
;
posp: bg POSP bg {
try ($1#posp $3)
with Bigraphs_are_not_disjoint ->
raise (Malformed_Posp (location (Parsing.rhs_start_pos 2)))}
;
psp: bg PSP bg {
try ($1#psp $3)
with Bigraphs_are_not_disjoint ->
raise (Malformed_Psp (location (Parsing.rhs_start_pos 2)))}
;
composition: bg COMPOSITION bg {
try ($1#composition $3)
with Bigraphs_have_no_common_interface ->
raise (Malformed_Composition (location (Parsing.rhs_start_pos 2)))}
A.2 Generazione del programma OCaml
A.2.1 Analizzatore lessicale
{
108 APPENDICE A. SORGENTI DEL COMPILATORE
open Dbplparser2ocaml
exception Unknown_Symbol of (int*int);;
let incr_linenum lexbuf =
let pos = lexbuf.Lexing.lex_curr_p in
lexbuf.Lexing.lex_curr_p <- { pos with
Lexing.pos_lnum = pos.Lexing.pos_lnum + 1;
Lexing.pos_bol = pos.Lexing.pos_cnum;
}
;;
}
let num = [’0’-’9’] | [’1’-’9’][’0’-’9’]+
let name = [’a’-’z’][’a’-’z’ ’A’-’W’ ’Y’-’Z’ ’0’-’9’]*
let sign = ’+’ | ’-’
rule token = parse
| [’ ’ ’\t’] { token lexbuf }
| ’\n’ { incr_linenum lexbuf; token lexbuf }
| "merge" { MERGE }
| "Signature" { SIGNATURE }
| "active"
| "passive"
| "atomic" as sigtype { SIGTYPE (sigtype) }
| "in" { IN }
| "let" { LET }
| num as n { NUM(n) }
| name as nm { NAME(nm) }
| sign as sn { SIGN (String.make 1 sn) }
| "=" { EQ }
| "[" { LSB }
| "]" { RSB }
| "," { COMMA }
| ":" { COLON }
| ";" { SEMICOLON }
| "*" { TENSOR }
| "°" { COMPOSITION }
| "/\\" { OSP }
A.2. GENERAZIONE DEL PROGRAMMA OCAML 109
| "\\/" { ISP }
| "||" { SP }
| "/^\\" { POSP }
| "|" { PSP }
| "/" { SUBSTITUTION }
| "\\" { FUSION }
| "(" { LRB }
| ")" { RRB }
| "X" { CLOSURE }
| "@" { PERMUTATION }
| eof { EOF }
| _ { raise (Unknown_Symbol (lexbuf.Lexing.lex_curr_p.Lexing.pos_lnum,
(lexbuf.Lexing.lex_curr_p.Lexing.pos_cnum-
lexbuf.Lexing.lex_curr_p.Lexing.pos_bol))) }
A.2.2 Parser
%{
open String
open Algebra
open Bigraph
exception Unbound_Name of (int*int)
exception Unbound_Bigraph of (int*int)
exception Malformed_Dbpl of (int*int)
exception Malformed_Signature of (int*int)
exception Malformed_Signature_Element of (int*int)
exception Malformed_SiteSet of (int*int)
exception Malformed_SiteSet_Element of (int*int)
exception Malformed_IonLinkList of (int*int)
exception Malformed_IonLinkList_Element of (int*int)
exception Malformed_NameSet of (int*int)
exception Malformed_NameSet_Element of (int*int)
exception Malformed_Bigraph of (int*int)
exception Malformed_Merge of (int*int)
exception Malformed_Ion of (int*int)
exception Malformed_Substitution of (int*int)
110 APPENDICE A. SORGENTI DEL COMPILATORE
exception Malformed_Fusion of (int*int)
let ecount = ref (-1);;
let vcount = ref (-1);;
let sign = new signatures;;
let bigraphs = Hashtbl.create 16;;
let invbigraphs = Hashtbl.create 16;;
let bgcount = ref 0;;
let ids = Hashtbl.create 16;;
let rec perm str n = match str with
| [] -> []
| head::tail ->
("(" ^head^ ", "^string_of_int n^")")::perm tail (n+1)
;;
let rec makeSigSet data = match data with
| [] -> "\n"
| (t, n, a)::tail -> sign#addsign2 n t (int_of_string a);
"sign#addsign2 \""^n^"\" \""^t^"\" "^a^";\n"^ makeSigSet tail
;;
let rec donames list c = match list with
| [] -> []
| head::tail ->
if (get head 0 = c)
then ("\"" ^ (sub head 1 (length head -1))^"\"")::donames tail c
else donames tail c
;;
let rec makelinklist l n = match l with
| [] -> []
| head::tail ->
("(" ^ string_of_int n ^ ", \"" ^
(sub head 1 (length head -1)) ^ "\")")::
makelinklist tail (n+1)
;;
let location pos = (pos.Lexing.pos_lnum, pos.Lexing.pos_cnum-pos.Lexing.pos_bol);;
let parse_error msg = Printf.eprintf "%s\n" msg
%}
%token <string> NUM
A.2. GENERAZIONE DEL PROGRAMMA OCAML 111
%token <string> NAME
%token <string> SIGTYPE
%token <string> SIGN
%token EOF LET IN EQ
%token TENSOR COMPOSITION OSP ISP SP POSP PSP
%token SUBSTITUTION FUSION PERMUTATION
%token LRB RRB LSB RSB COLON SEMICOLON COMMA
%token MERGE CTRLLIST CLOSURE SIGNATURE
%left TENSOR OSP ISP SP POSP PSP
%left COMPOSITION
%nonassoc CLOSURE PERMUTATION
%nonassoc SUBSTITUTION FUSION
%nonassoc MERGE
%start dbpl
%type <string> dbpl
%%
dbpl: signature letblock EOF {
"open Algebra\nopen Bigraph\n \
let compiledsignature = fun () ->
let sign = new signatures in\n" ^ $1 ^"sign;;\n \
let compiledbigraph = fun () ->"^ $2 ^ ";;\n" }
| error EOF {
raise (Malformed_Dbpl (location (Parsing.rhs_start_pos 1))) }
;
letblock: bg { $1 }
| letelem IN letblock { $3 }
;
letelem: LET NAME EQ bg {
if (Hashtbl.mem invbigraphs $4)
then Hashtbl.replace ids $2 (Hashtbl.find invbigraphs $4)
else Hashtbl.add bigraphs !bgcount $4;
Hashtbl.replace ids $2 !bgcount;
Hashtbl.add invbigraphs $4 !bgcount;
bgcount:=!bgcount+1 }
;
signature: SIGNATURE LSB RSB SEMICOLON { makeSigSet [] }
112 APPENDICE A. SORGENTI DEL COMPILATORE
| SIGNATURE LSB sigelem RSB SEMICOLON { makeSigSet $3 }
| error {
raise (Malformed_Signature (location (Parsing.rhs_start_pos 1))) }
;
sigelem: SIGTYPE NAME COLON NUM { ($1, $2, $4)::[] }
| SIGTYPE NAME COLON NUM COMMA sigelem { ($1, $2, $4)::$6}
| error {
raise (Malformed_Signature_Element (location (Parsing.rhs_start_pos 1))) }
;
siteset: LSB RSB { [] }
| LSB siteelem RSB { $2 }
| error {
raise (Malformed_SiteSet (location (Parsing.rhs_start_pos 1))) }
;
siteelem: NUM { $1::[] }
| NUM COMMA siteelem { $1::$3 }
| error {
raise (Malformed_SiteSet_Element (location (Parsing.rhs_start_pos 1))) }
;
ctrllist: LSB RSB { [] }
| LSB ctrlelem RSB { $2 }
| error {
raise (Malformed_IonLinkList (location (Parsing.rhs_start_pos 1))) }
;
ctrlelem: SIGN NAME { ($1^$2)::[] }
| SIGN NAME COMMA ctrlelem { ($1^$2)::$4 }
;
nset: LSB nsetelem RSB { $2 }
| error {
raise (Malformed_NameSet (location (Parsing.rhs_start_pos 1))) }
;
nsetelem: NAME COMMA NAME { $1::[$3] }
| nsetelem COMMA NAME { $1@[$3] }
| error {
raise (Malformed_NameSet_Element (location (Parsing.rhs_start_pos 1))) }
;
A.2. GENERAZIONE DEL PROGRAMMA OCAML 113
bg: LRB bg RRB { $2 }
| merge { $1 }
| ion { $1 }
| substitution { $1 }
| fusion { $1 }
| closure { $1 }
| permutation { $1 }
| tensor { $1 }
| osp { $1 }
| isp { $1 }
| sp { $1 }
| posp { $1 }
| psp { $1 }
| composition { $1 }
| NAME {
try (Hashtbl.find bigraphs(Hashtbl.find ids $1))
with Not_found ->
raise (Unbound_Bigraph (location (Parsing.rhs_start_pos 1))) }
;
merge: MERGE NUM { "merge " ^ $2 }
| error NUM {
raise (Malformed_Merge (location (Parsing.rhs_start_pos 1))) }
| MERGE error {
raise (Malformed_Merge (location (Parsing.rhs_start_pos 2))) }
| MERGE NUM error {
raise (Malformed_Merge (location (Parsing.rhs_start_pos 3))) }
;
ion: NAME ctrllist {
vcount:=!vcount+1;
let nm1=donames $2 ’-’ in
let nm=if (nm1=[]) then "[]" else "["^concat ";" nm1^"]" in
let dbg = "(discreteion \"" ^ ("v" ^ string_of_int !vcount) ^
"\" \"" ^ $1 ^ "\" "^nm^" [" ^ concat ";" (donames $2 ’+’) ^
"] [" ^ concat ";" (makelinklist $2 0) ^ "] sign" in
if (sign#activity $1="atomic")
then (dbg^"#composition ((merge 0)#tensor (id 1 "^nm^" []))")
114 APPENDICE A. SORGENTI DEL COMPILATORE
else dbg^")" }
substitution: NAME SUBSTITUTION NAME {
"substitution [\""^$3^"\"] \""^$1^"\"" }
| NAME SUBSTITUTION nset {
"substitution [\""^concat "\";\"" $3^"\"] \""^$1^"\"" }
| NAME SUBSTITUTION {
"emptysubstitution [" ^ $1 ^ "]" }
| nset SUBSTITUTION {
"emptysubstitution [\""^concat "\";\"" $1^"\"]" }
| nset SUBSTITUTION NAME {
raise (Malformed_Substitution (location (Parsing.rhs_start_pos 1))) }
| nset SUBSTITUTION nset {
raise (Malformed_Substitution (location (Parsing.rhs_start_pos 1))) }
;
fusion: NAME FUSION NAME { "fusion \""^$3^"\" [\""^$1^"\"]" }
| nset FUSION NAME { "fusion "^$3^" [\""^concat "\";\"" $1^"\"]" }
| FUSION NAME { "emptyfusion ["^$2^"]" }
| FUSION nset { "emptyfusion [\""^concat "\";\"" $2^"\"]" }
| NAME FUSION nset {
raise (Malformed_Fusion (location (Parsing.rhs_start_pos 3))) }
| nset FUSION nset {
raise (Malformed_Fusion (location (Parsing.rhs_start_pos 3))) }
;
closure: NAME CLOSURE NAME {
ecount:=!ecount+1;
"(closure \""^$3^"\" \""^$1^"\" \""^("e" ^ string_of_int !ecount)^"\")" }
| nset CLOSURE NAME {
ecount:=!ecount+1;
" (fusion \"1unused\" [\""^concat "\";\"" $1^"\"])#composition
(closure \""^$3^"\" \"1unused\" \""^("e" ^ string_of_int !ecount)^"\")" }
| NAME CLOSURE nset {
ecount:=!ecount+1;
" (closure \"2unused\" \""^$1^"\" \""^
("e" ^ string_of_int !ecount)^"\")#composition
(substitution [\""^concat "\";\"" $3^"\"] \"2unused\")"}
A.2. GENERAZIONE DEL PROGRAMMA OCAML 115
| nset CLOSURE nset {
ecount:=!ecount+1;
" ((fusion \"1unused\" [\""^concat "\";\"" $1^"\"])#composition
(closure \"2unused\" \"1unused\" \""^
("e" ^ string_of_int !ecount)^"\"))#composition
(substitution [\""^concat "\";\"" $3^"\"] \"2unused\")" }
| NAME CLOSURE {
ecount:=!ecount+1;
"upclosure "^$1^" "^("e" ^ string_of_int !ecount) }
| CLOSURE NAME {
ecount:=!ecount+1;
"downclosure "^$2^" "^("e" ^ string_of_int !ecount) }
| nset CLOSURE {
ecount:=!ecount+1;
"(fusion \"1unused\" [\""^concat "\";\"" $1^"\"])#composition
(upclosure \"1unused\" \""^("e" ^ string_of_int !ecount)^"\")" }
| CLOSURE nset {
"(downclosure \"2unused\" \""^ ("e" ^ string_of_int !ecount)^
"\")#composition (substitution [\""^concat "\";\"" $2^"\"] \"2unused\")"}
;
permutation: PERMUTATION siteset {
"permutation ["^concat "\";\"" (perm $2 0)^"]" }
;
tensor: bg TENSOR bg { "(("^$1^")#tensor ("^$3^"))" }
;
osp: bg OSP bg { "(("^$1^")#osp ("^$3^"))" }
;
isp: bg ISP bg { "(("^$1^")#isp ("^$3^"))" }
;
sp: bg SP bg { "(("^$1^")#sp ("^$3^"))" }
;
posp: bg POSP bg { "(("^$1^")#posp ("^$3^"))" }
;
psp: bg PSP bg { "(("^$1^")#psp ("^$3^"))" }
;
composition: bg COMPOSITION bg { "(("^$1^")#composition ("^$3^"))" }
116 APPENDICE A. SORGENTI DEL COMPILATORE
;
A.3 Interfaccia del compilatore
let compilefromfile file =
let inChannel = open_in file in
let lexbuf = Lexing.from_channel inChannel in
Dbplparser.dbpl Dbpllexer.token lexbuf;;
let compilefromstring string =
let lexbuf = Lexing.from_string string in
Dbplparser.dbpl Dbpllexer.token lexbuf;;
let compilefromfile2ocaml filein fileout =
let inChannel = open_in filein in
let outChannel = open_out fileout in
let lexbuf = Lexing.from_channel inChannel in
Printf.fprintf outChannel "%s\n" (Dbplparser2ocaml.dbpl Dbpllexer2ocaml.token lexbuf);;
let compilefromstring2ocaml string fileout =
let outChannel = open_out fileout in
let lexbuf = Lexing.from_string string in
Printf.fprintf outChannel "%s\n" (Dbplparser2ocaml.dbpl Dbpllexer2ocaml.token lexbuf);;
BSorgenti del decompilatore
let printDBPL signs bg =
"Signature ["^(concat "," (printsignature signs#getsigns))^"];\n"^
(printBG bg signs)^"\n";;
let rec printsignature signs = match signs with
| [] -> []
| (t, (act, ary))::tail ->
(act^" "^t^":"^string_of_int ary)::printsignature tail
;;
let printBG dbg signs=
let bg=Algebra.dnf dbg in
let w=List.hd bg in
let daux=List.hd (List.tl bg) in
let w1=List.hd (List.tl (List.tl bg)) in
let d = daux#composition
(w1#tensor (makebg (findsites daux#getprnt))) in
let wstr=printW w#returnlink d#getouterintn in
let dstr=printD d#returnprnt d#returnlink d#returnctrl
d#getouterintn signs in
if (wstr="")
then dstr
else (if (dstr="")
then wstr
else wstr^"°\n"^dstr);;
118 APPENDICE B. SORGENTI DEL DECOMPILATORE
let printW links inn =
let ltab = Hashtbl.create 32 in
let destinations = singlelist (linksdest ltab links) in
let w=printW2 destinations ltab in
"("^
if (w=[])
then (if (inn!=0)
then "merge "^string_of_int inn
else "")
else (concat "*" w)^
(if (inn!=0)
then "* merge "^string_of_int inn
else "")^")";;
let rec printW2 links ltab = match links with
| [] -> []
| (d, td)::tail ->
(printW3 d td (Hashtbl.find_all ltab d)) :: printW2 tail ltab
;;
let printW3 d td linkssources =
let n1=namesoftype linkssources "onm" in
let n2=namesoftype linkssources "inp" in
let top=(if (List.length n1 = 0)
then ""
else if (List.length n1 = 1)
then List.hd n1
else "["^concat "," n1 ^"]") in
let bottom=(if (List.length n2 = 0)
then ""
else if (List.length n2 = 1)
then List.hd n2
else "["^concat "," n2 ^"]") in
if (td="e")
then top^"X"^bottom
else if (td="onp")
119
then d^"/"^bottom
else top^"\\"^d
;;
let printD prn lnk ctrl nr signs =
let roots = makerootlist (nr-1) [] in
let ptab = hashprnt (Hashtbl.create 16) prn in
let ltab = hashlink (Hashtbl.create 16) lnk in
let d = printroots roots ptab ltab ctrl signs in
let l = passthroughlink lnk in
if (d=[])
then (if (l=[])
then ""
else "("^concat "*" l ^")")
else (if (l=[])
then ("("^concat "*" d ^")")
else ("(("^concat "*" d ^")*("^concat "*" l ^"))"));;
let rec printroots roots ptab ltab ctrl signs = match roots with
| [] -> []
| head::tail ->
let rsons = Hashtbl.find_all ptab (Root(head)) in
if (rsons=[])
then "merge 0"::printroots tail ptab ltab ctrl signs
else let sns = printson rsons ptab ltab ctrl signs [] in
if (sns=[])
then printroots tail ptab ltab ctrl signs
else ("("^concat "|" sns^")")::
printroots tail ptab ltab ctrl signs;;
let rec printson sons ptab ltab ctrl signs downlinks =
match sons with
| [] -> if (downlinks=[])
then []
else ["("^concat "*" (printlinks downlinks "-")^")"]
| Site(n)::tail ->
120 APPENDICE B. SORGENTI DEL DECOMPILATORE
("(merge 1@["^string_of_int n^"])")::
printson tail ptab ltab ctrl signs downlinks
| Node(n)::tail ->
let ary = List.assoc n ctrl in
let l = printionlinks (List.sort linkcompare
(Hashtbl.find_all ltab n)) in
let ionlinks = if (l=[])
then " []"
else " ["^concat "," l^"]" in
if (Hashtbl.mem ptab (Node(n)))
then let sl=(printlinks
(sonslink (Node(n)) ltab ptab) "+") in
("("^signs#typeof "active" ary^ionlinks^
(if (sl=[])
then ""
else "*"^concat "*" sl)^")°("^
concat "|" (printson
(Hashtbl.find_all ptab (Node(n)))
ptab ltab ctrl signs (filterlink
(Hashtbl.find_all ltab n) "inm"))^")")::
printson tail ptab ltab ctrl signs downlinks
else (signs#typeof "atomic" ary^ionlinks)::
printson tail ptab ltab ctrl signs downlinks
| head::tail -> printson tail ptab ltab ctrl signs downlinks
;;
let rec filterlink l t1= match l with
| [] -> []
| (p, d, t)::tail when t=t1 -> d::filterlink tail t1
| head::tail -> filterlink tail t1
;;
let rec sonslink f ltab ptab =
let sons = Hashtbl.find_all ptab f in
sonslink1 sons ltab ptab
and sonslink1 sons ltab ptab = match sons with
121
| [] -> []
| Node(n)::tail ->
(filterlink (Hashtbl.find_all ltab n) "onp")@
(sonslink (Node(n)) ltab ptab)@
sonslink1 tail ltab ptab
| head::tail -> sonslink1 tail ltab ptab
;;
let rec printionlinks l = match l with
| [] -> []
| (p, d, t)::tail ->
(if (t="onp")
then ("+"^d)
else ("-"^d))::printionlinks tail
;;
let rec printlinks l t = match l with
| [] -> []
| head::tail ->
if (t="-")
then (head^"\\"^head)::printlinks tail t
else (head^"/"^head)::printlinks tail t
;;
let linkcompare (p, d, t) (p1, d1, t1) =
if (p<p1)
then -1
else
if (p=p1)
then 0
else 1;;
let rec linksdest ltab links = match links with
| [] -> []
| (Name (s, ts), NameEdge (d, td))::tail ->
Hashtbl.add ltab d (s, ts);
122 APPENDICE B. SORGENTI DEL DECOMPILATORE
(d, td)::linksdest ltab tail
| head::tail -> linksdest ltab tail
;;
let rec namesoftype linkssources t = match linkssources with
| [] -> []
| (s, ts)::tail when (ts=t) -> s::namesoftype tail t
| head :: tail -> namesoftype tail t
;;
let rec hashprnt sons prn = match prn with
| [] -> sons
| SiteRoot (s, f)::tail ->
Hashtbl.add sons (Root(f)) (Site(s));
hashprnt sons tail
| NodeRoot (s, f)::tail ->
Hashtbl.add sons (Root(f)) (Node(s));
hashprnt sons tail
| NodeNode (s, f)::tail ->
Hashtbl.add sons (Node(f)) (Node(s));
hashprnt sons tail
| SiteNode (s, f)::tail ->
Hashtbl.add sons (Node(f)) (Site(s));
hashprnt sons tail
;;
let rec hashlink lnktab lnk = match lnk with
| [] -> lnktab
| (Port(p, n),NameEdge (d, t))::tail ->
Hashtbl.add lnktab n (p, d, t); hashlink lnktab tail
| head::tail -> hashlink lnktab tail
;;
let rec findsites p = match p with
| [] -> []
| (SiteNode (s,f))::tail -> s::findsites tail
123
| (SiteRoot (s,f))::tail -> s::findsites tail
| head::tail -> findsites tail;;
let rec makebgaux bg l = match l with
| [] -> ()
| head::tail -> bg#addprntsiteroot head head; makebgaux bg tail;;
let makebg l =
let bg = Algebra.id 0 [] [] in
bg#setinnerintn (List.length l);
bg#setouterintn (List.length l);
makebgaux bg l;
bg;;
let rec makerootlist n acc =
if (n>=0)
then makerootlist (n-1) (n::acc)
else acc;;
CSorgenti del visualizzatore
let dbg2svg bg =
let prn = bg#returnprnt in
let lnk = bg#returnlink in
let ptab = hashprnt (Hashtbl.create 16) prn in
let roots = makerootlist bg#getouterintn 0 in
let rawtree = if (bg#getouterintn = 0)
then ([Elem(Root(0), 0., 0., 60., 60.,[])])
else filltree roots ptab in
let edgelist = singlelist (edges lnk) in
let edgewidth = (float_of_int (List.fold_left (+) 0
(List.map (String.length) edgelist)))*.10. in
let linkslength = rootlinkslength rawtree lnk
(bg#getyminus) (bg#getyplus)
(bg#getxminus) (bg#getxplus) 0 in
let widthfixedtree = fixheight (fixsize rawtree
(maxvalue linkslength) edgewidth) in
let highest = (List.fold_right max (heights widthfixedtree) 0.0) in
let heigthfixedtree = setrootsheight widthfixedtree
(max highest (highest -.44.+.float_of_int
(List.length edgelist) *.30.)) in
let t = fixpos heigthfixedtree highest 0.0 edgewidth in
let namespos = placelinks t !allnames in
let edgedata = printedges edgelist (List.hd t) (highest -.44.) in
"<?xml version=\"1.0\" standalone=\"no\"?>\n<!DOCTYPE svg PUBLIC "^
"\"-//W3C//DTD SVG 1.1//EN\"\n\"http://www.w3.org/Graphics/SVG/1."^
"1/DTD/svg11.dtd\">\n<svg width=\"100%%\" height=\"100%%\" versio"^
126 APPENDICE C. SORGENTI DEL VISUALIZZATORE
"n=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\">\n<g transform=\""^
"translate(50,50)\">\n"^(printpgraph t)^
(printnames namespos)^String.concat "" (sndlist edgedata)
^(printlink t lnk namespos (firstlist edgedata) highest)^
"</g></svg>\n";;
let rec printpgraph t = match t with
| [] -> ""
| head:: tail -> printpgraph2 head^"\n"^printpgraph tail
and printpgraph2 e = match e with
| Elem(Root(n), x, y, rx, ry, sns) ->
"<rect width=\""^string_of_float rx^"\" "^
"height=\""^string_of_float ry^"\" x=\""^
string_of_float x^"\" y=\""^string_of_float y^
"\" style=\"fill:white;stroke:black;"^
"stroke-width:1;stroke-dasharray:9,5\"/>"^
(if (n!= (- 1))
then "\n<text x=\""^string_of_float (x +.5.)^
"\" y=\""^string_of_float (y +.15.)^"\""^
" font-size=\"15\">"^string_of_int n^
"</text>\n"
else "")^printpgraph sns
| Elem(Node(n), x, y, rx, ry, sns) ->
"<ellipse cx=\""^string_of_float x^
"\" cy=\""^string_of_float y^"\" rx=\""^
string_of_float (rx /.2.0)^"\" ry=\""^
string_of_float (ry /.2.0)^"\" "^
"style=\"fill:white;stroke:black;"^
"stroke-width:1\"/>\n<text x=\""^
string_of_float (x -.4.*.(float_of_int (String.length n)))^
"\" y=\""^string_of_float (y -.ry /.2.-.2.)^
"\" font-size=\"10\">"^n^"</text>\n"^
printpgraph sns
| Elem(Site(n), x, y, rx, ry, _) ->
"<rect width=\""^string_of_float rx^
"\" height=\""^string_of_float ry^"\" x=\""^
127
string_of_float x^"\" y=\""^
string_of_float y^"\" style=\"fill:#C3C3C3"^
";stroke:black;stroke-width:0\"/>\n<text x=\""^
string_of_float (x +.5.)^"\" y=\""^
string_of_float (y +.15.)^"\" font-size=\"1"^
"5\">"^string_of_int n^"</text>\n";;
let rec rootlinkslength t l ym yp xm xp s = match t with
| [] -> allnames:= singleallnodes !allnames; []
| (Elem(Root(n), x, y, rx, ry, sns)):: tail ->
let sons = allnodes sns in
let ypl = rootlinks sons l (getsetelem yp) "onp" in
let xml = rootlinks sons l (getsetelem xm) "inm" in
let yplfromname = difference (linksfromname l "onp") ypl in
let xmlfromname = difference (linksfromname l "inm") xml in
allnames:= (makelist ypl n "onp")@(!allnames);
allnames:= (makelist xml n "inm")@(!allnames);
if (s = 0)
then (allnames:= (makelist (getsetelem ym) n "onm")
@(!allnames);
allnames:= (makelist (getsetelem xp) n "inp")@(!allnames);
allnames:= (makelist yplfromname n "onp")@(!allnames);
allnames:= (makelist xmlfromname n "inm")@(!allnames);
(n, 12.*.float_of_int (List.fold_left (+) 0 (List.map
(String.length) (yplfromname@ypl@(getsetelem ym))))
+.6.*.float_of_int(List.length
(yplfromname@ypl@(getsetelem ym)) + 2),
12.*.float_of_int (List.fold_left (+) 0 (List.map
(String.length) (xmlfromname@xml@(getsetelem xp))))
+.6.*.float_of_int(List.length
(xmlfromname@xml@(getsetelem xp)) + 2))::
rootlinkslength tail l ym yp xm xp 1)
else (n, 12.*.float_of_int (List.fold_left (+) 0 (List.map
(String.length) ypl))
+.6.*.float_of_int(List.length ypl + 2),
128 APPENDICE C. SORGENTI DEL VISUALIZZATORE
12.*.float_of_int (List.fold_left (+) 0 (List.map
(String.length) xml))
+.6.*.float_of_int(List.length xml + 2))::
rootlinkslength tail l ym yp xm xp 1
| head:: tail -> rootlinkslength tail l ym yp xm xp 1;;
let rec placelinks2 l t b h = match l with
| [] -> []
| (n, (r, x, y, ty)):: tail when ty ="onm" or ty ="onp" ->
(n, (t, 0., ty))::
placelinks2 tail (t +.6.+.12.*.float_of_int
(String.length n)) b h
| (n, (r, x, y, ty)):: tail when ty ="inm" or ty ="inp" ->
(n, (b, (h +.16.), ty))::
placelinks2 tail t (b +.6.+.12.*.float_of_int
(String.length n)) h
| head:: tail -> placelinks2 tail t b h;;
let allnamescmp (n, (r, x, y, t)) (n1, (r1, x1, y1, t1)) =
if (t = t1)
then 0
else if ((t ="onm" or t ="inp") & (t1 ="onp" or t1 ="inm"))
then - 1
else 1;;
let rec placelinks t nms = match t with
| [] -> []
| (Elem(Root(n), x, y, rx, ry, sns)):: tail ->
placelinks2 (List.sort (allnamescmp) (thisrootlinks n nms))
(x +.15.) (x +.18.) ry@placelinks tail nms
| _ -> [];;
let rec printedges2 l x y = match l with
| [] -> []
| head:: tail -> ((head, (x, y)),
"<text x=\""^string_of_float x^"\" y=\""^string_of_float y^
129
"\" style=\"font-size:15\">"^head^"</text>\n")::
printedges2 tail (x +.8.*.float_of_int(String.length head))
(y +.30.);;
let printedges e (Elem(a, x, y, rx, ry, sns)) h =
if (sns =[]) then printedges2 e 30. h else
match (List.hd sns) with
(Elem(b, x1, y1, rx1, ry1, sns1)) ->
printedges2 e (x1 +.rx1 /.2.+.5.) h;;
let printlink t l nms edg h =
let ltab = Hashtbl.create 16 in
hashtree t ltab (countocc (Hashtbl.create 16) l);
Random.self_init();
printlink2 l ltab nms edg h;;
let rec printlink2 l tab nms edg h = match l with
| [] -> ""
| (Port(p, n), NameEdge(d, td)):: tail when td = "onp" ->
let (x, y, rx, ry, xs, tsp, b, bsp, el, esp, er) =
Hashtbl.find tab n in
let ys = eqtn x y (rx /.2.) (ry /.2.) xs (- 1.) in
let (x1, yd) = findinlist d nms "onp" in
let xd = x1 +.5.*.float_of_int (String.length d) in
Hashtbl.replace tab n (x, y, rx, ry, xs +.tsp,
tsp, b, bsp, el, esp, er);
"<polyline points=\""^
string_of_float xs^","^string_of_float ys^" "^
string_of_float xs^","^string_of_float (ys -.20.)^" "^
string_of_float xd^","^string_of_float (yd +.10.)^" "^
string_of_float xd^","^string_of_float yd^" "^
string_of_float (xd -.5.)^","^string_of_float (yd +.10.)
^" "^
string_of_float xd^","^string_of_float yd^" "^
string_of_float (xd +.5.)^","^string_of_float (yd +.10.)^
130 APPENDICE C. SORGENTI DEL VISUALIZZATORE
"\" style=\"fill:none;stroke:black;stroke-width:1\"/>\n"
^printlink2 tail tab nms edg h
| (Port(p, n), NameEdge(d, td)):: tail when td = "inm" ->
let (x, y, rx, ry, t, tsp, xs, bsp, el, esp, er) =
Hashtbl.find tab n in
let ys = eqtn x y (rx /.2.) (ry /.2.) xs 1. in
let (x1, yd) = findinlist d nms "inm" in
let xd = x1 +.5.*.float_of_int (String.length d) in
Hashtbl.replace tab n (x, y, rx, ry, t, tsp,
xs +.bsp, bsp, el, esp, er);
"<polyline points=\""^
string_of_float xs^","^string_of_float ys^" "^
string_of_float xs^","^string_of_float (ys +.20.)^" "^
string_of_float xd^","^string_of_float (yd -.26.)^" "^
string_of_float xd^","^string_of_float (yd -.16.)^" "^
string_of_float (xd -.5.)^","^string_of_float (yd -.26.)
^" "^
string_of_float xd^","^string_of_float (yd -.16.)^" "^
string_of_float (xd +.5.)^","^string_of_float (yd -.26.)
^" "^
"\" style=\"fill:none;stroke:black;stroke-width:1\"/>\n"^
printlink2 tail tab nms edg h
| (Port(p, n), NameEdge(d, td)):: tail when td = "e" ->
let (x, y, rx, ry, t, tsp, b, bsp, el, esp, er) =
Hashtbl.find tab n in
let (xd, yd) = List.assoc d edg in
let newel=if (x<xd) then el else el+.esp in
let newer=if (x<xd) then er-.esp else er in
let rnd=(Random.float 40.) -. 10. in
Hashtbl.replace tab n (x, y, rx, ry, t, tsp,
b, bsp, newel, esp, newer);
(if (x < xd)
then ("<polyline points=\""^string_of_float er^","^
string_of_float (eqtn x y (rx /.2.) (ry /.2.) er 1.)
^" "^
131
string_of_float er^","^string_of_float (h -.25. +.rnd)^" "^
string_of_float xd^","^string_of_float yd^
"\" style=\"fill:none;stroke:black;stroke-width:1\"/>\n")
else ("<polyline points=\""^string_of_float el^","^
string_of_float (eqtn x y (rx /.2.) (ry /.2.) el 1.)^" "^
string_of_float el^","^string_of_float (h -.36.+.rnd)^" "^
string_of_float (xd +.10.*.float_of_int(String.length d))
^","^
string_of_float (yd -.8.)^
"\" style=\"fill:none;stroke:black;stroke-width:1\"/>\n"))^
printlink2 tail tab nms edg h
| (Name(s, ts), NameEdge(d, td)):: tail when ts ="onm" & td ="inm" ->
let (xa, y1) = findinlist s nms "onm" in
let (xb, y2) = findinlist d nms "inm" in
let x1 = xa +.5.*.float_of_int (String.length s) in
let x2 = xb +.5.*.float_of_int (String.length d) in
"<polyline points=\""^
string_of_float x1^","^string_of_float y1^" "^
string_of_float x2^","^string_of_float (y2 -.26.)^" "^
string_of_float x2^","^string_of_float (y2 -.16.)^" "^
string_of_float (x2 -.5.)^","^string_of_float (y2 -.26.)^" "^
string_of_float x2^","^string_of_float (y2 -.16.)^" "^
string_of_float (x2 +.5.)^","^string_of_float (y2 -.26.)^
"\" style=\"fill:none;stroke:black;stroke-width:1\"/>\n"^
printlink2 tail tab nms edg h
| (Name(s, ts), NameEdge(d, td)):: tail when ts ="onm" & td ="e" ->
let (xa, y1) = findinlist s nms "onm" in
let (x2, y2) = List.assoc d edg in
let x1 = xa +.5.*.float_of_int (String.length s) in
"<polyline points=\""^
string_of_float x1^","^string_of_float y1^" "^
string_of_float x1^","^string_of_float (h -.40.)^" "^
string_of_float x2^","^string_of_float y2^
"\" style=\"fill:none;stroke:black;stroke-width:1\"/>\n"^
printlink2 tail tab nms edg h
132 APPENDICE C. SORGENTI DEL VISUALIZZATORE
| (Name(s, ts), NameEdge(d, td)):: tail when ts ="inp" & td ="onp" ->
let (xa, y1) = findinlist s nms "inp" in
let (xb, y2) = findinlist d nms "onp" in
let x1 = xa +.5.*.float_of_int (String.length s) in
let x2 = xb +.5.*.float_of_int (String.length d) in
"<polyline points=\""^
string_of_float x1^","^string_of_float (y1 -.16.)^" "^
string_of_float x1^","^string_of_float 20.^" "^
string_of_float x2^","^string_of_float (y2 +.10.)^" "^
string_of_float x2^","^string_of_float y2^" "^
string_of_float (x2 -.5.)^","^string_of_float (y2 +.10.)^" "^
string_of_float x2^","^string_of_float y2^" "^
string_of_float (x2 +.5.)^","^string_of_float (y2 +.10.)^
"\" style=\"fill:none;stroke:black;stroke-width:1\"/>\n"^
printlink2 tail tab nms edg h
| (Name(s, ts), NameEdge(d, td)):: tail when ts ="inp" & td ="e" ->
let (xa, y1) = findinlist s nms "inp" in
let (x2, y2) = List.assoc d edg in
let x1 = xa +.5.*.float_of_int (String.length s) in
"<polyline points=\""^
string_of_float x1^","^string_of_float (y1 -.16.)^" "^
string_of_float x1^","^string_of_float (h -.25.)^" "^
string_of_float x2^","^string_of_float y2^" "^
"\" style=\"fill:none;stroke:black;stroke-width:1\"/>\n"^
printlink2 tail tab nms edg h
| head:: tail -> printlink2 tail tab nms edg h;;
let rec hashprnt sons prn = match prn with
| [] -> sons
| SiteRoot (s, f):: tail -> Hashtbl.add sons (Root(f)) (Site(s));
hashprnt sons tail
| NodeRoot (s, f):: tail -> Hashtbl.add sons (Root(f)) (Node(s));
hashprnt sons tail
| NodeNode (s, f):: tail -> Hashtbl.add sons (Node(f)) (Node(s));
133
hashprnt sons tail
| SiteNode (s, f):: tail -> Hashtbl.add sons (Node(f)) (Site(s));
hashprnt sons tail;;
let rec filltree2 e ptab =
let sons = Hashtbl.find_all ptab e in match e with
| Root(n) -> Elem(Root(n), 0.0, 0.0, 0.0, 0.0, filltree sons ptab)
| Node(n) -> Elem(Node(n), 0.0, 0.0, 0.0, 0.0, filltree sons ptab)
| Site(n) -> Elem(Site(n), 0.0, 0.0, 45.0, 30.0,[])
and filltree roots ptab = match roots with
| [] -> []
| head:: tail -> filltree2 head ptab:: filltree tail ptab;;
let rec fixsize2 e l minspaz= match e with
| Elem(Root(n), x, y, rx, ry,[]) ->
Elem(Root(n), x, y, max (List.assoc n l) minspaz, 40.0,[])
| Elem(Node(n), x, y, rx, ry,[]) ->
Elem(Node(n), x, y, 45.0, 30.0,[])
| Elem(Site(n), x, y, rx, ry, _) ->
Elem(Site(n), x, y, 45.0, 30.0,[])
| Elem(Root(n), x, y, rx, ry, sns) ->
let xs1 = xsize sns l minspaz in
let minsize = List.assoc n l in
let xs = (max (fst(xs1) +.60.0 *.float_of_int (1 + List.length sns))
minsize)+.minspaz in
Elem(Root(n), x, y, xs , xs /.1.5, snd(xs1))
| Elem(Node(n), x, y, rx, ry, sns) ->
let xs1 = xsize sns l minspaz in
let xs = fst(xs1) +.30.0 *.float_of_int (1 + List.length sns) in
Elem(Node(n), x, y, xs, xs /.1.5, snd(xs1))
and fixsize t l minspaz= match t with
| [] -> []
| head:: tail -> fixsize2 head l minspaz:: fixsize tail l minspaz
and xsize t l minspaz = let t1 = fixsize t l minspaz in (xsize1 t1, t1)
and xsize1 t = match t with
| [] -> 0.0
134 APPENDICE C. SORGENTI DEL VISUALIZZATORE
| Elem(_, x, y, rx, ry, _):: tail -> rx +.xsize1 tail;;
let rec heights t = match t with
| [] -> []
| Elem(_, _, _, _, ry, _):: tail -> ry:: heights tail;;
let rec fixheight ts = match ts with
| [] -> []
| Elem(Root(n), x, y, rx, ry, sns):: tail ->
let newry = List.fold_right max (heights sns) 0.0 in
Elem(Root(n), x, y, rx, newry +.120.0, sns):: fixheight tail
| head:: tail -> head:: fixheight tail;;
let rec setrootsheight t h = match t with
| [] -> []
| Elem(e, x, y, rx, ry, sns):: tail ->
Elem(e, x, y, rx, h, sns):: setrootsheight tail h;;
let rec fixpos2 e h l minspaz rootsp = match e with
| Elem(Root(n), x, y, rx, ry, sns) ->
let sons = fixpos3 sns h ((max 60.0 minspaz) +.l) 2.0 minspaz rootsp in
Elem(Root(n), l, y, rx, ry, sons)
| Elem(Node(n), x, y, rx, ry, sns) ->
let sons = fixpos3 sns h (30.0 +.l) 1.0 minspaz rootsp in
Elem(Node(n), l +.rx /.2.0, h /.2.0, rx, ry, sons)
| Elem(Site(n), x, y, rx, ry, _) ->
Elem(Site(n), l, h /.2.0 -.15.0, rx, ry,[])
and fixpos3 sns h l m minspaz rootsp= match sns with
| [] -> []
| Elem(a, x, y, rx, ty, sns):: tail ->
fixpos2 (Elem(a, x, y, rx, ty, sns)) h l minspaz 0.::
fixpos3 tail h (l +.(max (minspaz*.rootsp) (30.0 *.m)) +.rx)
m minspaz rootsp;;
let rec fixpos t h l minspaz = match t with
| [] -> []
135
| Elem(a, x, y, rx, ry, sns):: tail ->
(fixpos2 (Elem(a, x, y, rx, ry, sns)) h l minspaz 1.)::
fixpos tail h (l +.rx +.20.0) minspaz;;
let rec allnodes s = match s with
| [] -> []
| Elem(Root(n), x, y, rx, ry, sns):: tail -> allnodes tail
| Elem(Site(n), x, y, rx, ry, sns):: tail -> allnodes tail
| Elem(Node(n), x, y, rx, ry, sns):: tail ->
[n]@(allnodes sns)@allnodes tail;;
let rec filterlinksfromport l s ty = match l with
| [] -> []
| (Port(p, n), NameEdge(d, td)):: tail when (is_in_list n s & td = ty) ->
d:: filterlinksfromport tail s ty
| head:: tail -> filterlinksfromport tail s ty;;
let rec nonportlinks l ty = match l with
| [] -> []
| (Name(s, ts), NameEdge(d, td)):: tail when td = ty ->
(d, td):: nonportlinks tail ty
| head:: tail -> nonportlinks tail ty;;
let eqtn x y a b p m =
y +.sqrt(b ** 2.-.b ** 2./.a ** 2.*.(p -.x) ** 2.) *.m;;
let rec thisrootlinks root nms = match nms with
| [] -> []
| (n, (r, x, y, t)):: tail when root = r ->
(n, (r, x, y, t)):: thisrootlinks root tail
| head:: tail -> thisrootlinks root tail;;
let rec printnames l = match l with
| [] -> ""
| (n, (x, y, t)):: tail ->
"<text x=\""^string_of_float x^"\" y=\""^string_of_float y^
136 APPENDICE C. SORGENTI DEL VISUALIZZATORE
"\" style=\"font-size:15\">"^n^"</text>\n"^printnames tail;;
let rec findinlist e l ty = match l with
| [] -> (nan, nan)
| (n, (x, y, t)):: tail when n = e & t = ty -> (x, y)
| head:: tail -> findinlist e tail ty;;
let rec countocc tab l = match l with
| [] -> tab
| (Port(p, n), NameEdge(d, td)):: tail when td ="onp" ->
if (Hashtbl.mem tab n)
then (let (t, b, e) = Hashtbl.find tab n in
Hashtbl.replace tab n (t +.1., b, e))
else Hashtbl.add tab n (1., 0., 0.); countocc tab tail
| (Port(p, n), NameEdge(d, td)):: tail when td ="inm" ->
if (Hashtbl.mem tab n)
then (let (t, b, e) = Hashtbl.find tab n in
Hashtbl.replace tab n (t, b +.1., e))
else Hashtbl.add tab n (0., 1., 0.); countocc tab tail
| (Port(p, n), NameEdge(d, td)):: tail when td ="e" ->
if (Hashtbl.mem tab n)
then (let (t, b, e) = Hashtbl.find tab n in
Hashtbl.replace tab n (t, b, e +.1.))
else Hashtbl.replace tab n (0., 0., 1.); countocc tab tail
| head:: tail -> countocc tab tail;;
let rec hashtree t tab occ = match t with
| [] -> ()
| Elem(Node(n), x, y, rx, ry, sns):: tail ->
if (Hashtbl.mem occ n)
then let (t, b, e) = Hashtbl.find occ n in
Hashtbl.add tab n (x, y, rx, ry, x -.rx /.2.+.10., (rx -.20.) /.t,
x -.rx /.2.+.10., (rx -.20.) /.b,x -.rx /.2., 20./.e, x +.rx /.2.);
hashtree sns tab occ;
hashtree tail tab occ
else hashtree sns tab occ;
137
hashtree tail tab occ
| Elem(Root(n), x, y, rx, ry, sns):: tail ->
hashtree sns tab occ;
hashtree tail tab occ
| head:: tail -> hashtree tail tab occ;;
let rec edges l = match l with
| [] -> []
| (Port(p, n), NameEdge(d, t)):: tail when (t ="e") -> d:: edges tail
| (Name(s, ts), NameEdge(d, td)):: tail when (td ="e") -> d:: edges tail
| head:: tail -> edges tail;;
let rec rootlinks s l nms ty = match nms with
| [] -> []
| head:: tail ->
if (is_in_list head (filterlinksfromport l s ty))
then head:: rootlinks s l tail ty
else rootlinks s l tail ty;;
let rec makelist l n t = match l with
| [] -> []
| head:: tail -> (head, (n, 0., 0., t)):: makelist tail n t;;
let rec in_allnodes_list e l = match l with
| [] -> false
| (n, (r, x, y, t)):: tail -> if (n = e) then true else in_allnodes_list e tail;;
let rec singleallnodes l = match l with
| [] -> []
| (n, (r, x, y, t)):: tail when not(in_allnodes_list n tail) ->
(n, (r, x, y, t)):: singleallnodes tail
| head:: tail -> singleallnodes tail;;
let rec linksfromname l t = match l with
| [] -> []
| (Name(s, ts), NameEdge(d, td)):: tail when t = td -> d:: linksfromname tail t
138 APPENDICE C. SORGENTI DEL VISUALIZZATORE
| head:: tail -> linksfromname tail t;;
let rec difference l1 l2 = match l1 with
| [] -> []
| head:: tail when (not (is_in_list head l2)) -> head:: difference tail l2
| head:: tail -> difference tail l2;;
let rec maxvalue l = match l with
| [] -> []
| (n, t, b):: tail -> (n, max t b):: maxvalue tail;;
let rec makerootlist total acc =
if (acc < total)
then Root(acc):: makerootlist total (acc + 1)
else [];;
Bibliografia
[1] Lars Birkedal, Troels Christoffer Damgaard, Arne J. Glenstrup, and Robin Mil-
ner. Matching of bigraphs. Electr. Notes Theor. Comput. Sci., 175(4):3–19, 2007.
7
[2] Lars Birkedal, Thomas Hildebrandt, Arne J. Glenstrup, Mikkel Bund-
gaard, Troels C. Damgaard, Søren Debois, Ebbe Elsborg, and Espen
Højsgaard. Bigraphical programming language, 2004. Available at
http://www.itu.dk/research/pls/wiki/index.php/Bigraphical_
Programming_Languages_(BPL). 1
[3] Davide Grohmann and Marino Miculan. An algebra for directed bigraphs. In
Ian Mackie and Detlef Plump, editors, Pre-proceedings of TERMGRAPH 2007,
ENTCS. Elsevier, 2007. 1, 1, 2.3.1
[4] Davide Grohmann and Marino Miculan. Directed bigraphs. In Proc. XXIII
MFPS, volume 173 of ENTCS, pages 121–137. Elsevier, 2007. 1, 1, 1, 2.2
[5] O. H. Jensen and Robin Milner. Bigraphs and mobile processes (revised). Techni-
cal report UCAM-CL-TR-580, Computer Laboratory, University of Cambridge,
2004. 2.1
[6] Ole Høgh Jensen and Robin Milner. Bigraphs and transitions. In POPL, pages
38–49, 2003. 1
[7] Robin Milner. Bigraphical reactive systems. In CONCUR, pages 16–35, 2001. 1
[8] Robin Milner. Pure bigraphs: Structure and dynamics. Information and
Computation, 204(1):60–122, 2006. 1, 2.1.1
[9] Raul Pellarini. Implementazione dei Bigrafi Diretti: strutture dati, algebra e
relative pushout. ., 2008. 1