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