Francesco Romano, Lexedit XXI: uno strumento di analisi - ittig

Transcript

Francesco Romano, Lexedit XXI: uno strumento di analisi - ittig
Istituto di Teoria e Tecniche dell’Informazione Giuridica
Consiglio Nazionale delle Ricerche
Lexedit XXI: uno strumento di analisi e correzione di testi normativi.
Lexedit XXI: a check instrument for legislative texts.
Il lavoro si propone di descrivere Lexedit XXI, il suo funzionamento le regole di correzione e le prime verifiche effettuate su
un campione di leggi. Si descrivono inoltre i primi problemi analizzati nell’uso del sistema per quanto riguarda la conversione
strutturale del testo nel formato XML previsto dalla DTD NIR.
check - instrument - legislative - texts
Francesco Romano
Rapporto tecnico n. 10/2005
Firenze
1
Premessa
Il lavoro che è riportato nelle pagine che seguono si propone di descrivere sinteticamente il
funzionamento dello strumento di analisi e correzione Lexedit XXI.
Tale strumento, elaborato assieme ai ricercatori CELI di Torino, costituisce lo sviluppo del software di
aiuto al drafting Lexedit, recentemente aggiornato per la Camera dei Deputati, discostandosene però sia
per l’architettura completamente ridisegnata, sia per due nuove caratteristiche:
- marcatura XML dei testi analizzati validabile con la DTD NIR1;
- possibilità per l’utente d’implementare nuove regole di riconoscimento delle strutture testuali e di
segnalazione degli errori.
Nelle pagine seguenti descriverò inoltre le regole di correzione strutturale implementate di default nel
prototipo, alcuni problemi evidenziatisi in sede di conversione strutturale del testo legislativo dal formato
originario al formato XML previsto dalla DTD NIR e le regole di correzione strutturale implementate per
segnalare alcuni di questi problemi.
Verranno infine illustrate alcune regole di correzione strutturale e linguistica, implementate per rilevare
errori rispetto alle regole di tecnica legislativa, rilevanti per la formazione dell'indice di qualità delle leggi
regionali toscane.
1. Lexedit XXI.
La nuova architettura è disegnata tramite l’integrazione di più moduli che svolgono tre funzioni
principali:
1.1. Riconoscimento della struttura del testo
Questa funzione è svolta attraverso la marcatura automatica in XML degli elementi (articoli, commi,
lettere, ecc.) che compongono la struttura del documento normativo. Non è previsto che tali regole,
contenute nel file structConv-grammar.cup, siano modificabili dall’utente.
1.2. Segnalazione di errori strutturali
Una volta individuata la struttura, il sistema dispone di un modulo che individua e segnala errori
strutturali in base alle regole di tecnica legislativa (numerazioni errate, sequenze errate di partizioni, ecc.).
Tale modulo dispone di un work bench che permette all’utente di specificare, in base alle proprie esigenze,
l’implementazione di regole di riconoscimento e di messaggi di segnalazione di errori.
Tali regole sono basate su espressioni regolari con propria sintassi e sono contenute nel file
correzione_01.ugs.
1.3. Segnalazione di errori linguistici
Il modulo consente il riconoscimento e la segnalazione di strutture linguistiche complesse quali le
disposizioni normative (ad es. la novella, la delega legislativa, ecc.) ed anche di strutture ed espressioni
linguistiche portatrici di uno specifico valore semantico (ad es. riferimenti normativi, locuzioni designanti
il destinatario di un precetto, ecc.). Utilizza un parser linguistico (Sophia 2.1), che garantisce
l’applicazione di uno o più sistemi di regole volti all’identificazione sia di strutture “benformate”, sia di
1
Attualmente l'osservanza rispetto alla DTD è puramente teorica dal momento che non avviene validazione. Tuttavia, essendo i documenti
costruiti da un programma, la fase di validazione è omissibile. Nel caso in cui in futuro si vogliano rendere le DTD compatibili sarà necessario
sostituire gli elementi proprietari di Lexedit con delle processing instruction che rendano omogenei gli elementi delle due DTD. Infatti la
marcatura effettuata da Lexedit XXI risulta più "rilassata" rispetto a quella prevista dal DTD di Norme In Rete per due ragioni: la prima è quella
di permettere il marcamento delle modifiche individuato e definito nella analisi di Maria Chiara De Lorenzo (che prevede più elementi rispetto a
quelli, peraltro tuttora discussi, previsti da NIR). Il secondo motivo è che la DTD usata dal prototipo Lexedit XXI contiene elementi ulteriori volti
alla segnalazione di errori. Per quanto riguarda questi ultimi si tratta dell'inserzione di elementi tipo: <errorDesc id="err_20" message="Ogni
intestazione è composta da Art. seguito da numero cardinale"subtype="top">Ogni intestazione è composta da Art. seguito da numero
cardinale(err_20)</errorDesc> Essi sono ammessi praticamente dentro ogni elemento. Inoltre ogni elemento possiede un ulteriore attributo
lexID che viene utilizzato esclusivamente da LexEdit XXI.
2
strutture ed elementi difformi da quanto dettato dalle regole di tecnica legislativa o da quanto richiesto
dalle regole logico-linguistiche.
Anche il parser linguistico offre un work bench che consente all’utente di incrementare le regole di
riconoscimento e segnalazione.
2. Controlli di correttezza strutturale
Nei paragrafi seguenti la tabella sinottica mostra nella colonna di sinistra la regola di tecnica legislativa
che si vuole controllare con Lexedit XXI (che coinciderà anche con il messaggio di errore inviato dal
sistema), la colonna a destra evidenzia il formalismo nel quale la regola di tecnica legislativa è stata
tradotta.
Le regole sono divise seguendo le partizioni strutturali dell’articolato.
Sono considerate tipografiche le regole che descrivono particolarità ortografiche di stringhe o
caratteri secondo la prassi tipografica, mentre si ritengono strutturali quelle regole che descrivono
sequenze di stringhe secondo quanto disposto dalle regole di tecnica normativa2.
2.1 Regole strutturali dell'articolo:
R3=meta{type=struct}
La numerazione degli articoli è
cond{}
progressiva secondo la serie
check{Fornaturale dei numeri cardinali. (n2
= n1 + 1)
each(articolo)[][position()!=@num]}
error{e-id=err_04}
R6 = meta{type=struct}
Se il primo articolo ha la
cond{}
rubrica la devono avere anche
check{Fortutti gli altri.
each(articolo)[][count(rubrica)=0]}
error{e-id=err_06}
2.2 Regole tipografiche articolo
R20=
meta{type=char}
Ogni intestazione è composta
check{For-each(articolo)[][Forda Art. seguito da numero
each(num)[][text()!#"(\r|\r\n|\s)*Art\.\s[0cardinale
9]+(\s)*(\r|\r\n)*"]]}
error{e-id=err_20}
R20a=
meta{type=char}
check{For-each(articolo)[][Foreach(num)[][text()#"(\r|\r\n|\s)*art\.\s[09]+(\r|\r\n|\s)*"]]}
error{e-id=err_20b} // segnala che Art è
scritto min
R20b=
meta{type=char}
Le regole contenute nei paragrafi da 2.1 a 2.10 sono state implementate dai ricercatori CELI sulla base della analisi
preliminare fornita da ITTIG. In tal senso si veda: Pietro Mercatali, Francesco Romano, Analisi preliminare per la progettazione
di uno strumento di analisi e correzione di testi normativi, rapporto tecnico n. 11/2004, 10 pp. Firenze, Ittig-Cnr, 2004. Le regole
riportate nella nota del paragrafo 3 e al paragrafo 4 sono state invece scritte direttamente in ITTIG durante la fase di test del
sistema
2
3
check{For-each(articolo)[][Foreach(num)[][text()#"(\r|\r\n|\s)*Art\s[09]+(\r|\r\n|\s)*"]]}
error{e-id=err_20c} // segnala che manca
il punto
R86=
meta{type=char}
L’articolo deve terminare con
check{For-each(articolo)[][Forpunto
each(corpo)[position()=last()][text()!#"(.|
(\r|\r\n))+\.(\r|\r\n|\s)*"]]}
error{e-id=err_86}
2.3 Regole strutturali del comma:
Nell'articolo deve
almeno 1 comma
esserci
R24 = meta{type=struct}
Cond{}
check{For-each(articolo)
[][count(comma)=0]}
error{e-id=err_24}
R26 =meta{type=struct}
Se in un articolo ci sono più
cond{}
commi, i numeri devono essere in
check{For-each(articolo)[][Forsuccessione (n2 = n1 + 1).
each(comma)[][position()!=@num]]}
error{e-id=err_26}
2.4 Regole tipografiche del comma
R29=
meta{type=char}
Il numero del comma deve
check{For-each(comma)[][Foressere un cardinale seguito da
each(num)[][text()!#"(\r|\r\n|\s)*[0punto.
9]+\.(\s|\r|\r\n)*"]]}
error{e-id=err_29}
R30=
meta{type=char}
Ogni comma finisce con punto
check{Fore a capo.
each(comma)[][string()!§"\.(\r|\r\n|\n|
\s)*\Z"]}
error{e-id=err_30}
R32
= meta{type=char}
Se all’interno del comma ci
check{For-each(comma)[][Forsono due punti e a capo, la parte
che segue è una sequenza di each(corpo)[][text()§":(\r|\r\n|\n)"]]}
lettere
error{e-id=err_32}
R33 = meta{type=char}
Se all’interno del comma ci
sono due punti e a capo, la parte check{For-each(comma)[count(lettera)=0][Foreach(corpo)[][text()§":(\r|\r\n|\n)*\Z"]]}
che segue è una modifica.
error{e-id=err_32}
2.5 Regole strutturali della lettera
Le lettere stanno
all'interno del comma.
solo
R34=meta{type=struct}
cond{}
check{For-each(el)[][parent()!=comma]}
error{e-id=err_34}
4
Se il comma è suddiviso in
lettere devono esserci almeno due
lettere
Le lettere devono essere in
successione secondo l'ordine
alfabetico italiano. (a,b,c, ecc.).
Si usano solo le lettere
dell’alfabeto italiano (non quindi
le lettere j, k, w, x, y).
R35=meta{type=struct}
cond{}
check{For-each(comma)[][count(el)=1]}
error{e-id=err_35}
R36 =meta{type=struct}
cond{}
check{For-each(comma)[][Foreach(el)[][position()!=@num]]}
error{e-id=err_36}
R37 =meta{type=struct}
cond{}
check{For-each(el)[][Foreach(num)[][text()§"(j|J|k|K|w|W|x|X|y|Y)"]]}
error{e-id=err_37}
2.6 Regole tipografiche della lettera
R39=
meta{type=char}
Ogni lettera inizia con una
check{For-each(el)[][Forlettera minuscola dell’alfabeto
each(num)[][text()!#"(\r|\r\n|\s|\n)*[a-zAseguita da parentesi “a)”.
Z]+\)(\s|\r|\r\n|\n)*"]]}
error{e-id=err_39}
R40 = meta{type=char}
Non si va a capo all’interno
check{For-each(el)[][Fordella lettera se questa non
contiene una sequenza di numeri. each(corpo)[][text()§"(\r|\r\n|\n)[\S]"]]}
error{e-id=err_40}
R41=
meta{type=char}
Ogni lettera termina con punto
check{Fore virgola.
each(el)[position()!=last()][string()!§"\;(\
r|\r\n|\n|\s)*\Z"]}
error{e-id=err_41}
2.7 Regole strutturali del numero
I numeri sono solo all'interno
delle lettere.
Se la lettera è suddivisa in
numeri devono esserci almeno
due numeri.
I numeri dei numeri devono
essere in successione (n2 = n1 +
1)
R47=meta{type=struct} cond{}
check{For-each(en)[][parent()!=el]}
error{e-id=err_47}
R48=meta{type=struct}
cond{}
check{For-each(el)[][count(en)=1]}
error{e-id=err_48}
R49 =meta{type=struct}
cond{}
check{For-each(el)[][Foreach(en)[][position()!=@num]]}
error{e-id=err_49}
2.8 Regole tipografiche del numero
Ogni numero inizia con il
numero cardinale in cifre arabe
R50=
meta{type=char}
check{For-each(en)[][For5
seguito da parentesi “1)”
Ogni numero termina con
punto e virgola se segue un altro
numero.
Ogni numero termina con
punto e virgola se segue una
lettera.
Non si va a capo in un numero.
each(num)[][text()!#"(\r|\r\n|\s|\n)*[09]+\)(\s|\r|\r\n|\n)*"]]}
error{e-id=err_50}
R51=
meta{type=char}
check{Foreach(en)[position()!=last()][string()!§"\;(\
r|\r\n|\n|\s)*\Z"]}
error{e-id=err_51}
R52=
meta{type=char}
check{Foreach(el)[position()!=last()][Foreach(en)[position()=last()][string()!§"\;(\r
|\r\n|\n|\s)*\Z"]]}
error{e-id=err_52}
R87 = meta{type=char}
check{For-each(el)[][Foreach(corpo)[][text()§"(\r|\r\n|\n)"]]}
error{e-id=err_87}
2.9 Regole strutturali delle sovrapartizioni
Se c'è una sezione, deve essere
contenuta in un capo.
R71=meta{type=struct}
cond{}
check{For-
each(sezione)[][parent()!=capo]}
error{e-id=err_71}
R72a=meta{type=struct}
Se c'è un capo, ce ne deve
cond{}
essere almeno un altro
check{Foreach(articolato)[][count(capo)=1]}
error{e-id=err_72}
R72b=meta{type=struct}
cond{}
check{For-each(libro)[][count(capo)=1]}
error{e-id=err_72}
R72c=meta{type=struct}
cond{}
check{For-each(parte)[][count(capo)=1]}
error{e-id=err_72}
R72d=meta{type=struct}
cond{}
check{Foreach(titolo)[][count(capo)=1]}
error{e-id=err_72}
R73a=meta{type=struct}
Se c’è un titolo ce ne deve
cond{}
essere almeno un altro
check{Foreach(articolato)[][count(titolo)=1]}
error{e-id=err_73}
6
R73b=meta{type=struct}
cond{}
check{Foreach(libro)[][count(titolo)=1]}
error{e-id=err_73}
R73c=meta{type=struct}
cond{}
check{Foreach(parte)[][count(titolo)=1]}
error{e-id=err_73}
R74a=meta{type=struct}
Se c’è una parte ce ne deve
cond{}
essere almeno un’altra
check{Foreach(articolato)[][count(parte)=1]}
error{e-id=err_74}
R74b=meta{type=struct}
cond{}
check{Foreach(libro)[][count(parte)=1]}
error{e-id=err_74}
R75=meta{type=struct}
Se c’è un libro ce ne deve
cond{}
essere almeno un altro
check{Foreach(articolato)[][count(libro)=1]}
error{e-id=err_75}
Almeno un titolo deve essere
suddiviso in capi
R76a=meta{type=struct}
cond{}
check{Foreach(articolato)[count(titolo)!=0][count(tit
olo[count(capo)=1
||
count(capo)=0
])
=count(titolo)]}
error{e-id=err_76}
R76b=meta{type=struct}
cond{}
check{Foreach(libro)[count(titolo)!=0][count(titolo[c
ount(capo)=1
||
count(capo)=0
])
=count(titolo)]}
error{e-id=err_76}
R76c=meta{type=struct}
cond{}
check{Foreach(parte)[count(titolo)!=0][count(titolo[c
ount(capo)=1
||
count(capo)=0
])
=count(titolo)]}
error{e-id=err_76}
R77a=meta{type=struct}
Almeno una parte deve essere
7
suddivisa in titoli
Almeno un libro deve essere
suddiviso in parti
I numeri delle sovrapartizioni
devono essere in successione
all’interno
della
stessa
sovrapartizione (n2 = n1 + 1)
Titolo I
Capo I
Capo II
Titolo II
Capo I
Capo II
Se la prima sovrapartizione ha
la rubrica la devono avere anche
tutte le altre.
cond{}
check{Foreach(articolato)[count(parte)!=0][count(part
e[count(titolo)=1 || count(titolo)=0
])
=count(parte)]}
error{e-id=err_77}
R77b=meta{type=struct}
cond{}
check{Foreach(libro)[count(parte)!=0][count(parte[cou
nt(titolo)=1
||
count(titolo)=0
])
=count(parte)]}
error{e-id=err_77}
R78a=meta{type=struct}
cond{}
check{Foreach(articolato)[count(libro)!=0][count(libr
o[count(parte)=1
||
count(parte)=0
])
=count(libro)]}
error{e-id=err_78}
R79a =meta{type=struct}
cond{}
check{For-each(*)[][Foreach(libro)[][position()!=@num]]}
error{e-id=err_79}
R79b =meta{type=struct}
cond{}
check{For-each(*)[][Foreach(parte)[][position()!=@num]]}
error{e-id=err_79}
R79c =meta{type=struct}
cond{}
check{For-each(*)[][Foreach(titolo)[][position()!=@num]]}
error{e-id=err_79}
R79d =meta{type=struct}
cond{}
check{For-each(*)[][Foreach(capo)[][position()!=@num]]}
error{e-id=err_79}
R81a = meta{type=struct}
cond{
Exists(libro)[][count(rubrica)!=0]}
check{Foreach(libro)[][count(rubrica)=0]}
error{e-id=err_81}
R81b = meta{type=struct}
cond{
Exists(capo)[][count(rubrica)!=0]}
check{Foreach(capo)[][count(rubrica)=0]}
8
error{e-id=err_81}
R81c = meta{type=struct}
cond{
Exists(titolo)[][count(rubrica)!=0]}
check{Foreach(titolo)[][count(rubrica)=0]}
error{e-id=err_81}
R81d = meta{type=struct}
cond{
Exists(parte)[][count(rubrica)!=0]}
check{Foreach(parte)[][count(rubrica)=0]}
error{e-id=err_81}
R81e = meta{type=struct}
cond{
Exists(sezione)[][count(rubrica)!=0]}
check{Foreach(sezione)[][count(rubrica)=0]}
error{e-id=err_81}
2.10 Regole tipografiche delle sovrapartizioni
R85 = meta{type=char}
La sovrapartizione inizia con
check{For-each(el)[][For[nome-sovrapartizione](libro,
parte, titolo, capo, sezione) each(corpo)[][text()§".(\r|\r\n|\n)(Parte|
seguito da numero romano.
Titolo|Capo|Sezione|Libro)([
\t])+[^(I+|IV|
V|V(I)+|I?X|XI+)]*(\r|\r\n|\n)[^0-9]*\Z"]]}
error{e-id=err_85}
3. Punti critici nella conversione strutturale
Lexedit XXI ha evidenziato 2 tipi di problema nel riconoscimento della struttura dell'atto normativo.
A) Durante l'analisi di un primo campione di leggi regionali le regole contenute nel file che si occupa
della conversione strutturale del testo non sono state in grado di riconoscere determinate strutture.
1) Struttura non individuata che finisce dentro ad altra struttura marcata.
In tali casi in cui, dentro alla struttura marcata finisce qualcosa di diverso che non fa parte di quella
struttura (ad esempio una sovrapartizione nel corpo di un comma) e che ci sembra non possa essere che
un'altra struttura non riconosciuta, l'unico modo di intervenire è quello di scrivere regole di correzione che
all'interno della struttura marcata vadano ad individuare l'anomalia3.
Dopo una prima fase di test dello strumento, operata su un campione di leggi della regione Umbria, abbiamo direttamente
implementato alcune regole casistiche di correzione strutturale che di seguito riportiamo:
Regola 6bis: "Parte di testo non correttamente inserita nella struttura dell'articolo": R6bis=meta{type=struct}
cond{}
check{For-each(articolo)[][For-each(corpo)[][parent()!=comma]]}
error{e-id=err_06bis}Regola 33 quater: Errata scrittura di CapoR33quater=meta{type=char}check{Foreach(capo)[][For-each(num)[][text()§"(\.)"]]}
error{e-id=err_33quater}.Tale regola segnalerà un errore tutte le volte che nell'elemento Capo sia presente un punto.
3
9
Si potrebbero anche cambiare le regole di conversione della struttura, ma ci sembra che ci siano molte
controindicazioni, come in quei casi in cui si generi un errore in caso di strutture corrette.
2) Strutture e parti di testo non riconosciute e non marcate.
Sono quei casi in cui Lexedit non è riuscito a riconoscere e marcare parti di testo in fase di conversione
strutturale.
In questi casi dovremmo utilizzare una marcatura generica e residuale (il tag conv_err all’interno
dell’xml ma non visualizzata) che dia luogo ad un messaggio che avverta che quella certa parte di testo
apparentemente non appartiene alla struttura normativa.
In questo modo il sistema lancerebbe un warning evitandoci di dovere scrivere ulteriori regole che
comunque potremmo prevedere in una fase di correzione successiva se riteniamo che ci si trovi alla
presenza di un errore ricorrente che valga la pena di segnalare con maggiore precisione.
B) Abbiamo riscontrato infine casi in cui Lexedit XXI ha marcato una struttura in modo errato cioè
attribuendole un marcatore non proprio.
In questo casi non abbiamo al momento, alcuna soluzione proponibile.
E' tuttavia pensabile che talora siano le regole che formano la grammatica del parser a essere ambigue
mentre talaltra siano le strutture ad essere ambigue. In tali casi ci possiamo solo chiedere se sia possibile
individuare e tracciare i percorsi che il parser segue, per capire se tali percorsi siano standard o meno.
4 Regole per controllo qualità
In questo paragrafo si descrivono due regole di prova, create per verificare alcune fra le regole
contenute nell’allegato D del Manuale regionale di tecnica legislativa4.
Tali regole sono applicabili d’ufficio dai funzionari addetti agli uffici legislativi, senza cioè la necessità
di far tornare il testo di legge in aula per una nuova votazione, dopo l’avvenuta correzione.
Alcune fra queste regole applicabili d’ufficio, in particolare 21 fra esse, sono state adottate dai
funzionari degli uffici legislativi del Consiglio regionale della Toscana per il controllo di qualità della leggi
regionali toscane.
L’uso o il mancato uso di queste 21 regole concorre cioè a determinare l’indice di qualità della legge
regionale esaminata.
Abbiamo provato a implementare direttamente alcune fra queste regole mentre altre erano tra quelle
previste di default dal sistema.
Pensiamo che per verificare alcune fra esse sarà necessario scrivere regole di correzione strutturali o
tipografiche come quelle sotto descritte:
R88 = meta{type=char}
Intestare
gli
allegati
check{For-each(annesso)[][Forpropriamente detti con la
denominazione "Allegato" e each(num)[][text()§"[0-9]"]]}
contraddistinguerli con una lettera
error{e-id=err_88}
maiuscola.
R91 =meta{type=char}
Scrivere per esteso unità di
cond{}
misura e monetarie.
check{For-each(comma)[][Foreach(corpo)[][text()§"(Kg|Hg|Km)"]]}
error{e-id=err_91}
Il Manuale di tecnica legislativa cui si fa riferimento è il documento "Regole e suggerimenti per la redazione dei testi
normativi" voluto dalla Conferenza dei Presidenti dei Consigli Regionali e adottato a partire dal 1991 dalle regioni italiane nella
sua versione aggiornata nel 2002.
4
10
Per la verifica di altre regole si dovrà ricorrere al modulo di correzione linguistica e quindi al parser
linguistico Sophia 2.1.
Attraverso il comando Run presente nel work bench di Lexedit XXI sarà infatti possibile richiamare i
moduli di analisi linguistica presenti nel parser Sophia 2.1.
Sarà così applicata al file XML risultato dalla conversione strutturale, l'analisi della disposizione di
modifica testuale esplicita, l'analisi dei riferimenti presenti nel testo o l'analisi di specifici errori.
Nell'esempio sotto si riporta, nella sintassi del modulo di analisi semantica del parser Sophia 2.1, la
regola di riconoscimento di uno degli errori rilevato per calcolare l'indice di qualità delle leggi regionali
toscane5.
Scrivere numeri in cifre nelle
(NX)+((MISURA:vsigla)|
unità di misura in quelle (VALUTA:vsigla))
monetarie e nelle percentuali.
(Esempio di scrittura errata:
venti euro)
{actionNEW(
vsigla:rule:"prova",
vsigla:type:"error",
vsigla:id:"err_01",
vsigla:message:"errorescrittura-numeri",
vsigla:erBeg:start,
vsigla:erEnd:start)}
Le categorie semantiche MISURA e VALUTA identificheranno tutte le possibili denominazioni di unità di misura, di valute
e le percentuali. Quando tali categorie saranno precedute non da un numero (NX) il sistema rileverà l'errore classificato con un
certo ID (vsigla:id:"err_01") e lancerà un messaggio per l'utente (vsigla:message:"errore-scrittura-numeri").
5
11