% % This file was written in Literate Programming % % Authors: Pedro Henriques, Daniela da Cruz % Date: November, 2005 % Second Update: December, 2005 % Last update: April, 2006 (ANTLR version) - Rodrigo Baptista % \documentclass[a4paper]{report} \usepackage[portuges]{babel} \usepackage{a4wide} \usepackage[latin1]{inputenc} \usepackage{epsf} \usepackage{graphicx} \usepackage{html} \parindent=0pt \parskip=2pt \newtheorem{questao}{Quest\~{a}o} \def\site{\emph{site}} \def\internet{\textsf{Internet}} \def\sgml{\textsf{SGML}} \def\html{\textsf{HTML}} \def\dtd{\textsf{DTD}} \def\w3{\textsf{WWW}} \def\lisa{\textsf{LISA}} \def\java{\textsf{Java}} \title{Processamento de Linguagens I\\ LESI + LMCC (3$^{\circ}$ ano)} \author{Exerc\'{i}cios com Gram\'{a}ticas de Atributos\\ Sua implementa\c c\~{a}o em CoCo/R, LISA, Lex/Yacc, LRC e AntLR} \date{Language.Processing@@di.um.pt\\ \today} \begin{document} \maketitle \newpage \tableofcontents \newpage \chapter*{Pref\'{a}cio} Este documento, come\c{c}ou a ser escrito em Agosto de 2005, e \'{e} da autoria de \textsf{Daniela Carneiro da Cruz} e \textsf{Pedro Rangel Henriques}, respons\'aveis pela sua integra\c{c}\~ao e manuten\c{c}\~ao, tendo contudo o contributo de outros membros do \textsf{gEPL}\footnote{O grupo de Especifica\c{c}\~ao e Processamento de Linguagens do Departamento de Inform\'atica da Universidade do Minho, \url{www.di.uminho.pt/~gepl}.}, nomeadamente do \textsf{Rodrigo Baptista}.\\ O seu objectivo \'e duplo: \begin{itemize} \item por um lado, pretendemos mostrar a resolu\c{c}\~ao de um problema de defini\c{c}\~ao de uma linguagem e desenvolvimento do respectivo processador usando Gram\'aticas de Atributos (GA), mas de uma forma te\'orica e gen\'erica; para tal discutir-se-\'a a escolha dos atributos necess\'arios e a escrita das regras de c\'alculo, das condi\c{c}\~oes de contexto e das regras de tradu\c{c}\~ao. \item por outro lado, pretendemos ilustrar a sua implementa\c{c}\~ao com diferentes ferramentas de gera\c{c}\~ao de compiladores baseadas em GAs. \end{itemize} Para o efeito o documento est\'a organizado em v\'arios cap\'{\i}tulos.\\ No primeiro encontramos o enunciado de um problema que se pretende que seja, por um lado curto e claro, mas por outro que seja um caso real/\'{u}til; a sua resolu\c{c}\~ao deve ser tamb\'em clara e f\'{a}cil de assimilar para todos aqueles que se estejam a inicar na \'area.\\ Ap\'{o}s a descri\c c\~{a}o do problema, passamos \`{a} sua resolu\c c\~{a}o abstracta.\\ Posteriormente, nos cap\'{\i}tulos seguintes, apresentamos a implementa\c c\~{a}o dessa GA gen\'erica em diferentes geradores de compiladores.\\ Por fim s\~{a}o apresentados os resultados obtidos para um mesmo texto-fonte exemplo com cada um dos geradores de compiladores testados nos cap\'{\i}tulos precendentes. %--------------------------------------------------------------------------------------------- \chapter{Enunciado do exerc\'{i}cio} \textsf{Lavanda} \'{e} uma Linguagem de Dom\'{i}nio Espec\'{i}fico (\textsf{DSL}) que se destina a descrever as remessas de sacos de roupa que os Pontos de Recolha (PR) de uma Lavandaria enviam diariamente \'{a} Central (LC) para lavar. Cada saco tem um n\'{u}mero de identifica\c c\~{a}o e o nome do cliente que o deixou; o seu conte\'{u}do est\'{a} dividido em lotes. Cada lote corresponde a um tipo de pe\c ca (roupa-de-corpo, ou roupa-de-casa), um tipo de tinto (branco, ou cor) e um tipo de fio (algod\~{a}o, l\~{a} e fibra), registando-se ent\~{a}o o n\'{u}mero de pe\c cas entregues que pertencem a esse lote.\\ A gram\'{a}tica independente de contexto $G$, abaixo apresentada, define a linguagem \textsf{Lavanda} pretendida. O S\'{i}mbolo Inicial \'e \texttt{Lavanda}, os S\'{i}mbolos Terminais s\~{a}o escritos em min\'{u}sculas (pseudo-terminais), ou em mai\'{u}scula (palavras-reservadas), ou entre apostrofes (sinais de pontua\c c\~{a}o) e a string nula \'{e} denotada por \verb"&"; os restantes ser\~{a}o os S\'{i}mbolos N\~{a}o-Terminais. \begin{verbatim} p1: Lavanda --> Cabec Sacos p2: Cabec --> data IdPR p3: Sacos --> Saco '.' p4: | Sacos Saco '.' p5: Saco --> num IdCli Lotes p6: Lotes --> Lote Outros p7: Lote --> Tipo Qt p8: Tipo --> Classe Tinto Fio p9: Outros --> & p10: | ';' Lotes p11: IdPR --> id p12: IdCli --> id p13: Qt --> num p14,15: Classe--> corpo | casa p16,17: Tinto --> br | cor p18,19,20: Fio --> alg | la | fib \end{verbatim} %--------------------------------------------------------------------------- Depois de transformar $G$ numa gram\'{a}tica independente de contexto abstracta $Gabs$ (pode reduzir algumas produ\c c\~{o}es que lhe pare\c cam sup\'{e}rfluas), escreva uma \textbf{Gram\'{a}tica de Atributos}, $GA$, para: \begin{enumerate} \item calcular (e imprimir) o n\'{u}mero total de sacos enviados e n\'{u}mero de lotes de cada cliente. \item calcular e imprimir o total de pe\c cas de cada um dos 12 tipos de lotes (desde 'corpo/br/alg' at\'{e} 'casa/cor/fib') enviadas, para lavar, pelo PR \`{a} LC. \item calcular o custo total de cada saco, supondo que inicialmente \'{e} fornecida a tabela de pre\c cos do PR em causa (a tabela indica o pre\c co por pe\c ca para cada tipo de lote).\\ Nesta fase, deve haver ainda a preocupa\c c\~{a}o de detectar situa\c c\~{o}es de erro em que o n\'{u}mero de identifica\c c\~{a}o do saco seja repetido ou sempre que apare\c ca um saco para um cliente j\'{a} encontrado. \end{enumerate} %-------------------------------------------------------------------------- \chapter{Resolu\c c\~{a}o do exerc\'{i}cio} Nesta primeira fase, escrevemos a gram\'{a}tica abstracta $Gabs$.\\ Para tal eliminam-se todos os terminais sem carga sem\^{a}ntica (palavras-reservadas e sinais). A $G$ ser\'{a} simplificada eliminando produ\c c\~{o}es sem alternativas em que no lado direito s\'{o} aparece um terminal --- neste caso: \texttt{p11, p12, p13}. \begin{verbatim} p1a: Lavanda --> Cabec Sacos p2a: Cabec --> data id p3a: Sacos --> Saco p4a: | Sacos Saco p5a: Saco --> num id Lotes p6a: Lotes --> Lote Outros p7a: Lote --> Tipo num p8a: Tipo --> Classe Tinto Fio p9a: Outros --> & p10a: | Lotes p11a: Classe--> corpo p12a: | casa p13a: Tinto --> br p14a: | cor p15a: Fio --> alg p16a: | la p17a: | fib \end{verbatim} Para cada uma das tr\^{e}s al\'{i}neas , a resolu\c c\~{a} \'{e} feita em 2 passos.\\ O primeiro passo \'{e} escolher os atributos (classe, tipo) a associar aos s\'{i}mbolos para extrair a informa\c c\~{a}o pedida.\\ O segundo passo \'{e} escrever as regras de c\'{a}lculo para os s\'{i}mbolos respectivos. \section{Al\'{i}nea 1} Para esta al\'{i}nea ser\~{a}o precisos 2 atributos sintetizados: \begin{enumerate} \item \texttt{nSacos::int} associados a \texttt{Lavanda} e \texttt{Sacos}; \item \texttt{nLotes::int} associados a \texttt{Saco}, \texttt{Lotes} e \texttt{Outros}.\\ \end{enumerate} As regras de c\'{a}lculo e as regras de tradu\c c\~{a}o a associar \`{a}s produ\c c{o}es onde os s\'{i}mbolos em causa aparecem s\~{a}o: \begin{verbatim} p1a: Lavanda --> Cabec Sacos -- Lavanda.nSacos = Sacos.nSacos -- escreve( Lavanda.nSacos ) p3a: Sacos --> Saco -- Sacos.nSacos = 1 p4a: | Sacos Saco -- Sacos0.nSacos = Sacos1.nSacos + 1 p5a: Saco --> num id Lotes -- Saco.nLotes = Lotes.nLotes -- escreve( Saco.nLotes ) p6a: Lotes --> Lote Outros -- Lotes.nLotes = Outros.nLotes + 1 p9a: Outros --> & -- Outros.nLotes = 0 p10a: | Lotes -- Outros.nLotes = Lotes.nLotes \end{verbatim} \section{Al\'{i}nea 2} Para esta al\'{i}nea ser\~{a}o precisos 3 atributos: \begin{enumerate} \item \texttt{inEnv::HashTable} a \texttt{Saco}, \texttt{Lotes} e \texttt{Lote} --- atributo herdado; \item \texttt{outEnv::HashTable} a \texttt{Lavanda}, \texttt{Sacos}, \texttt{Saco}, \texttt{Lotes}, \texttt{Lote} e \texttt{Outros} --- atributo sintetizado; \item \texttt{name::String} a \texttt{Tipo}, \texttt{Classe}, \texttt{Tinto} e \texttt{Fio} --- atributo sintetizado. \end{enumerate} As regras de c\'{a}lculo e as regras de tradu\c c\~{a}o a associar \`{a}s produ\c c\~{o}es onde os s\'{i}mbolos em causa aparecem s\~{a}o: \begin{verbatim} p1a: Lavanda --> Cabec Sacos -- escreveT( Sacos.outEnv ) p3a: Sacos --> Saco -- Saco.inEnv = Sacos.inEnv -- Sacos.outEnv = Saco.outEnv p4a: | Sacos Saco -- Saco.inEnv = Sacos1.outEnv -- Sacos1.inEnv = Sacos0.inEnv -- Sacos0.outEnv = Saco.outEnv p5a: Saco --> num id Lotes -- Lotes.inEnv = Saco.inEnv -- Saco.outEnv = Lotes.outEnv p6a: Lotes --> Lote Outros -- Lote.inEnv = Lotes.inEnv -- Outros.inEnv = Lote.outEnv -- Lotes.outEnv = Outros.outEnv p7a: Lote --> Tipo num -- Lote.outEnv = updateTablePrice(Lote.inEnv, Tipo.name, num) p8a: Tipo --> Classe Tinto Fio -- Tipo.name = Classe.name + Tinto.name + Fio.name p9a: Outros --> & -- Outros.outEnv = Outros.inEv; p10a: | Lotes -- Lotes.inEnv = Outros.inEnv; -- Outros.outEnv = Lotes.outEnv; p11a: Classe --> corpo -- Classe.name = "corpo" p12a: Classe --> casa -- Classe.name = "casa" p13a: Tinto --> br -- Tinto.name = "br" p14a: Tinto --> cor -- Tinto.name = "cor" p15a: Fio --> alg -- Fio.name = "alg" p16a: Fio --> la -- Fio.name = "la" p17a: Fio --> fib -- Fio.name = "fib" \end{verbatim} \section{Al\'{i}nea 3} Para esta al\'{i}nea ser\~{a}o precisos 5 atributos: \begin{enumerate} \item \texttt{inTable::HashTable} aos s\'{i}mbolos \texttt{Sacos}, \texttt{Saco}, \texttt{Lotes}, \texttt{Lote} e \texttt{Outros} --- atributo herdado (Tabela de prec cos); \item \texttt{inIds::Vector} aos s\'{i}mbolos \texttt{Sacos} e \texttt{Saco} --- atributo herdado (Array de identificadores de clientes); \item \texttt{outIds::Vector} aos s\'{i}mbolos \texttt{Sacos} e \texttt{Saco} --- atributo sintetizado (Array de identificadores de clientes); \item \texttt{custoTotal::int} aos s\'{i}mbolos \texttt{Saco}, \texttt{Lotes}, \texttt{Lote} e \texttt{Outros} --- atributo sintetizado (Custo de cada saco); \item \texttt{name::string} aos s\'{i}mbolos \texttt{Tipo}, \texttt{Classe}, \texttt{Tinto} e \texttt{Fio} --- atributo sintetizado (Nome de cada atributo associado ao s\'{i}mbolo Tipo). \end{enumerate} As regras de c\'{a}lculo e as regras de tradu\c c\~{a}o a associar \`{a}s produ\c c\~{o}es onde os s\'{i}mbolos em causa aparecem s\~{a}o: \begin{verbatim} p1a : Lavanda --> Cabec Sacos -- Sacos.inTable = initTable() -- Sacos.inIds = initIds() p3a: Sacos --> Saco -- Saco.inTable = Sacos.inTable -- Saco.inIds = Sacos.inIds -- Sacos.outIds = Saco.outIds -- escrevePreco( Saco.custoTotal ) p4a: Sacos --> Sacos Saco -- Saco.inTable = Sacos0.inTable -- Sacos1.inEnv = Sacos0.inEnv -- Saco.inIds = Sacos1.outIds -- Sacos1.inIds = Sacos0.inIds -- Sacos0.outIds = Saco.outIds -- escrevePreco( Saco.custoTotal ) p5a: Saco --> num id Lotes -- Saco.outEnv = novoId( Saco.inIds, num.value() ) -- if ( pertence( num,Saco.inIds ) ) erro("Numero de saco repetido!") -- Lotes.inTable = Saco.inTable -- Saco.custoTotal = Lotes.custoTotal p6a: Lotes --> Lote Outros -- Lote.inTable = Lotes.inTable -- Outros.inTable = Lotes.inTable -- Lotes.custoTotal = Lote.custoTotal + Outros.custoTotal p7a: Lote --> Tipo num -- Lote.custoTotal = lookupPreco( Lote.inEnv, Tipo.name ) * num.value() p8a: Tipo --> Classe Tinto Fio -- Tipo.name = Classe.name + Tinto.name + Fio.name p9a: Outros --> & -- Outros.custoTotal = 0 p10a: | Lotes -- Outros.custoTotal = Lotes.custoTotal p11a: Classe --> corpo -- Classe.name = "corpo" p12a: Classe --> casa -- Classe.name = "casa" p13a: Tinto --> br -- Tinto.name = "br" p14a: Tinto --> cor -- Tinto.name = "cor" p15a: Fio --> alg -- Fio.name = "alg" p16a: Fio --> la -- Fio.name = "la" p17a: Fio --> fib -- Fio.name = "fib" \end{verbatim} \chapter{Implementa\c c\~{a}o \textsf{CoCoR}} \section{Gram\'{a}tica - Scanner, Parser \& Evaluator} Toda a gram\'{a}tica de atributos, incluindo a especifica\c c\~{a}o l\'{e}xica, \'{e} escrita num \'{u}nico ficheiro ("Lavanda.atg"), que se lista a seguir. Os atributos s\~{a}o declarados em cada produ\c c\~{a}o junto dos respectivos símbolos entre '$<$' e '$>$'. As regras de c'{a}lculo s~{a}o escritas em C\# ao longo da produ\c c\~{a}o entre "(." e ".)".\\ @o Lavanda.atg @{ COMPILER Lavanda /******************************************************************** * SCANNER * *********************************************************************/ COMPILER Lavanda public OpHashtable opTable; // --------------- IGNORECASE CHARACTERS letter = 'a'..'z'. digit = '0'..'9'. TOKENS ident = letter { letter }. number = digit { digit }. IGNORE '\r' + '\n' + '\t' @} @o Lavanda.atg @{ /******************************************************************** * PARSER * *********************************************************************/ PRODUCTIONS /*--------------------------- PRODUCTION FOR GRAMMAR NAME -----------------*/ Lavanda (. int nSacos = 0; Hashtable inTable = opTable.initNLotes(), outTable, inTPrice = opTable.initTablePrice(); ArrayList inIds = opTable.initIds(), outIds = inIds; .) = Cabec Sacos (. Console.WriteLine("Total sacos: " + nSacos + "\n"); opTable.printTableLotes(outTable); .). Cabec = Data ident. Data (. int day, month, year; .) = number (. day = Convert.ToInt32(t.val); if (day < 1 || day > 31) SemErr("Wrong day!"); .) '-' number (. month = Convert.ToInt32(t.val); if (month < 1 || month > 12) SemErr("Wrong month!"); .) '-' number (. year = Convert.ToInt32(t.val); if (year < 2000 || day > 2010) SemErr("Wrong year!"); .). Sacos (. int nSacos1 = 0; float custoTotal; .) = Saco (. nSacos = 1; .) [ Sacos (. nSacos += nSacos1; .) ]. Saco (. int nLotes = 0, num; custoTotal = 0; .) = number (. num = Convert.ToInt32(t.val); .) ident '(' Lotes ')' (. Console.WriteLine("N. lotes no saco " + num + ": " + nLotes); opTable.writePrice(custoTotal); if ( inIds.Contains( num )) throw new Exception("Number of Bag already exists!"); outIds = opTable.newId(inIds, num); .). @} @o Lavanda.atg @{ Lotes (. int nLotes1 = 0; float custoTotal2 = 0; .) = Lote Outros (. nLotes = nLotes1 + 1; custoTotal += custoTotal2; .). Lote (. string name = ""; .) = Tipo number (. outTable = opTable.updateTableLotes( inTable, name, Convert.ToInt32(t.val)); custoTotal = opTable.lookupPrice(inTPrice, name) * Convert.ToInt32(t.val); .). Outros (. nLotes = 0; outTable = inTable; custoTotal = 0; .) = [ ',' Lotes ]. Tipo (. string name1 = "", name2 = "", name3 = ""; .) = Classe '-' Tinto '-' Fio (. name = name1 + "-" + name2 + "-" + name3; .). Classe (. name = ""; .) = "corpo" (. name = "corpo"; .) | "casa" (. name = "casa"; .). Tinto (. name = ""; .) = "br" (. name = "br"; .) | "cor" (. name = "cor"; .). Fio (. name = ""; .) = "alg" (. name = "alg"; .) | "la" (. name = "la"; .) | "fib" (. name = "fib"; .). END Lavanda. @} \section{Fun\c c\~{o}es auxiliares} As fun\c c\~{o}es auxiliares sobre tabelas de Hash (nomeadamente tabela de pre\c cos foram definidas no seguinte m\'{o}dulo: @o OpHashtable.cs @{ using System; using System.Collections; namespace Lavanda { public class OpHashtable { public OpHashtable() { } // alinea b public Hashtable initNLotes() { Hashtable env = new Hashtable(); env.Add("corpo-br-la",0); env.Add("corpo-br-alg",0); env.Add("corpo-br-fib",0); env.Add("corpo-cor-la",0); env.Add("corpo-cor-alg",0); env.Add("corpo-cor-fib",0); env.Add("casa-br-la",0); env.Add("casa-br-alg",0); env.Add("casa-br-fib",0); env.Add("casa-cor-la",0); env.Add("casa-cor-alg",0); env.Add("casa-cor-fib",0); return env; } public Hashtable updateTableLotes(Hashtable inTable, String name, int number) { IDictionaryEnumerator env = inTable.GetEnumerator(); while(env.MoveNext()) { string key = (string)env.Key; if (key.Equals(name)) { int pieces = (int)env.Value + number; inTable.Remove(key); inTable.Add(key, pieces); break; } } return inTable; } @} @o OpHashtable.cs @{ public void printTableLotes( Hashtable myList ) { IDictionaryEnumerator myEnumerator = myList.GetEnumerator(); Console.WriteLine( "\t-Descricao-\t-N. Lotes-" ); while ( myEnumerator.MoveNext() ) Console.WriteLine("\t{0}:\t{1}", myEnumerator.Key, myEnumerator.Value); Console.WriteLine(); } // alinea c public Hashtable initTablePrice() { Hashtable env = new Hashtable(); env.Add("corpo-br-la",1.0f); env.Add("corpo-br-alg",2.2f); env.Add("corpo-br-fib",3.4f); env.Add("corpo-cor-la",4.5f); env.Add("corpo-cor-alg",3.7f); env.Add("corpo-cor-fib",1.9f); env.Add("casa-br-la",2.6f); env.Add("casa-br-alg",5.3f); env.Add("casa-br-fib",7.1f); env.Add("casa-cor-la",3.5f); env.Add("casa-cor-alg",2.5f); env.Add("casa-cor-fib",2.3f); return env; } public float lookupPrice (Hashtable inPrice, string name ) { float price = 0; IDictionaryEnumerator env = inPrice.GetEnumerator(); while ( env.MoveNext() ) { if (env.Key.Equals(name)) { price = (float)env.Value; break; } } return price; } @} @o OpHashtable.cs @{ public ArrayList initIds() { return ( new ArrayList() ); } public ArrayList newId(ArrayList old, int num) { old.Add(num); return ( (ArrayList)old.Clone() ); } public void writePrice(float num) { Console.WriteLine("Preco Total: " + num + "\n" ); } } } // end namespace @} \break \chapter{Implementa\c c\~{a}o \textsf{LISA}} No caso do gerador \lisa, descreve-se toda a gram\'{a}tica de atributos, incluindo a especifica\c c\~{a}o l\'{e}xica num ficheiro \'{u}nico --- Lavanda.lisa --- apresentado a seguir.\\ Os atributos s\~{a}o declarados explicitamente uma vez, antes das produ\c c\~{o}es.\\ As regras de c\'{a}lculo escrevem-se em \java \ no fim de cada produ\c c\~{a}o num bloco "\{ \}" pr\'{o}prio.\\ O c\'{o}digo dos m\'{e}todos \java \ auxiliares s\~{a}o inclu\'{i}dos no mesmo ficheiro, no fim das regras sem\^{a}nticas.\\ @o Lavanda.lisa @{ language Lavanda { lexicon { ReservedWord corpo | casa | br | cor | alg | fib | la Number [0-9]+ Data [0-2][0-9]\-[0-9][0-9]\-[0-2][0-9][0-9][0-9] Identifier [a-z]+ separa \( | \) | \, | \> | \- ignore [\0x09\0x0A\0x0D\ ]+ } attributes int LAVANDA.nSacos, CABEC.nSacos, SACOS.nSacos, SACO.nLotes, LOTES.nLotes, LOTE.nLotes, OUTROS.nLotes; String TIPO.name, CLASSE.name, TINTO.name, FIO.name, LAVANDA.output, SACOS.output, SACO.output; // b) Hashtable SACOS.inTable, SACO.inTable, OUTROS.inTable, LOTE.inTable, LOTES.inTable, SACOS.outTable, SACO.outTable, OUTROS.outTable, LOTE.outTable, LOTES.outTable; // c) Hashtable SACOS.inTablePrice, SACO.inTablePrice, LOTES.inTablePrice, LOTE.inTablePrice, OUTROS.inTablePrice; Vector SACOS.inIds, SACO.inIds, SACOS.outIds, SACO.outIds; double SACO.custoTotal, LOTES.custoTotal, OUTROS.custoTotal, LOTE.custoTotal; @} @o Lavanda.lisa @{ rule Lavanda { LAVANDA ::= CABEC SACOS compute { LAVANDA.nSacos = SACOS.nSacos; LAVANDA.output = "\n\nSACOS:" + escreve( LAVANDA.nSacos, "sacos" ) + "\n\nLOTES: " + SACOS.output + "\n" + "TABELA LOTES: " + printTable(SACOS.outTable); // b) SACOS.inTable = initNLotes(); // c) SACOS.inTablePrice = initTablePrice(); SACOS.inIds = initIds(); }; } rule Cabec { CABEC ::= #Data #Identifier compute { CABEC.nSacos = 0; }; } rule Sacos1 { SACOS ::= SACO compute { SACOS.nSacos = 1; SACOS.output = SACO.output + writePrice( SACO.custoTotal ); // b) SACO.inTable = SACOS.inTable; SACOS.outTable = SACO.outTable; // c) SACO.inTablePrice = SACOS.inTablePrice; SACO.inIds = SACOS.inIds; SACOS.outIds = SACO.outIds; }; } @} @o Lavanda.lisa @{ rule Sacos2 { SACOS ::= SACOS SACO compute { SACOS[0].nSacos = SACOS[1].nSacos + 1; SACOS[0].output = SACOS[1].output + SACO.output + writePrice( SACO.custoTotal ); // b) SACO.inTable = SACOS[1].outTable; SACOS[1].inTable = SACOS[0].inTable; SACOS[0].outTable = SACO.outTable; // c) SACO.inTablePrice = SACOS[0].inTablePrice; SACOS[1].inTablePrice = SACOS[0].inTablePrice; SACO.inIds = SACOS[1].outIds; SACOS[1].inIds = SACOS[0].inIds; SACOS[0].outIds = SACO.outIds; }; } rule Saco { SACO ::= #Number #Identifier \( LOTES \) compute { SACO.nLotes = LOTES.nLotes; SACO.output = escreve( SACO.nLotes, "lotes" ); // b) LOTES.inTable = SACO.inTable; SACO.outTable = LOTES.outTable; // c) SACO.outIds = newId( SACO.inIds, Integer.valueOf( #Number.value()).intValue() ); LOTES.inTablePrice = SACO.inTablePrice; SACO.custoTotal = LOTES.custoTotal; if ( inIds.contains( Integer.valueOf(#Number.value()).intValue() )) throw new Exception("Cliente already exists!"); }; } rule Lotes { LOTES ::= LOTE OUTROS compute { LOTES.nLotes = OUTROS.nLotes + 1; // b) LOTE.inTable = LOTES.inTable; OUTROS.inTable = LOTE.outTable; LOTES.outTable = OUTROS.outTable; // c) LOTE.inTablePrice = LOTES.inTablePrice; OUTROS.inTablePrice = LOTES.inTablePrice; LOTES.custoTotal = LOTE.custoTotal + OUTROS.custoTotal; @} @o Lavanda.lisa @{ }; } rule Lote { LOTE ::= TIPO #Number compute { LOTE.nLotes = 0; // b) LOTE.outTable = updateTablePrice(LOTE.inTable, TIPO.name, Integer.valueOf(#Number.value()).intValue()); // c) LOTE.custoTotal = lookupPrice( LOTE.inTablePrice, TIPO.name ) * (Integer.valueOf(#Number.value()).intValue()); }; } rule Tipo { TIPO ::= CLASSE \- TINTO \- FIO compute { TIPO.name = CLASSE.name + "/" + TINTO.name + "/" + FIO.name; }; } rule Outros { OUTROS ::= compute { OUTROS.nLotes = 0; // b) OUTROS.outTable = OUTROS.inTable; // c) OUTROS.custoTotal = 0; } | \, LOTES compute { OUTROS.nLotes = LOTES.nLotes; // b) LOTES.inTable = OUTROS.inTable; OUTROS.outTable = LOTES.outTable; // c) OUTROS.custoTotal = LOTES.custoTotal; LOTES.inTablePrice = OUTROS.inTablePrice; }; } @} @o Lavanda.lisa @{ rule Classe { CLASSE ::= corpo compute { CLASSE.name = "corpo"; } | casa compute { CLASSE.name = "casa"; }; } rule Tinto { TINTO ::= br compute { TINTO.name = "br"; } | cor compute { TINTO.name = "cor"; }; } rule Fio { FIO ::= alg compute { FIO.name = "alg"; } | la compute { FIO.name = "la"; } | fib compute { FIO.name = "fib"; }; } method Print { import java.util.*; public String escreve(int num, String descripton) { String str = "\n\nNumero de " + descripton + ": " + num; return str; } // b) @} @o Lavanda.lisa @{ public Hashtable initNLotes() { Hashtable env = new Hashtable(); env.put("corpo/br/la",0); env.put("corpo/br/alg",0); env.put("corpo/br/fib",0); env.put("corpo/cor/la",0); env.put("corpo/cor/alg",0); env.put("corpo/cor/fib",0); env.put("casa/br/la",0); env.put("casa/br/alg",0); env.put("casa/br/fib",0); env.put("casa/cor/la",0); env.put("casa/cor/alg",0); env.put("casa/cor/fib",0); return env; } public Hashtable updateTablePrice(Hashtable inTable, String name, int number) { inTable = (Hashtable)inTable.clone(); int pieces = ((Integer)inTable.get(name)).intValue(); inTable.remove(name); inTable.put(name,number+pieces); return inTable; } public String printTable(Hashtable inTable) { String out="\n\n", str=""; for (Enumeration et = inTable.keys(); et.hasMoreElements();) { str = (String)et.nextElement(); int pieces = ((Integer)inTable.get(str)).intValue(); out += str + " ----> " + pieces + "\n"; } return out; } @} @o Lavanda.lisa @{ // c) public Hashtable initTablePrice() { Hashtable env = new Hashtable(); env.put("corpo/br/la",1.0); env.put("corpo/br/alg",2.2); env.put("corpo/br/fib",3.4); env.put("corpo/cor/la",4.5); env.put("corpo/cor/alg",3.7); env.put("corpo/cor/fib",1.9); env.put("casa/br/la",2.6); env.put("casa/br/alg",5.3); env.put("casa/br/fib",7.1); env.put("casa/cor/la",3.5); env.put("casa/cor/alg",2.5); env.put("casa/cor/fib",2.3); return env; } public double lookupPrice ( Hashtable in, String name ) { return ( ((Double)in.get(name)).doubleValue() ); } public Vector initIds() { return ( new Vector() ); } public Vector newId(Vector old, int num) { old.addElement(num); return ( (Vector)old.clone() ); } public String writePrice(double num) { return ( "\n\nPreco Total: " + num + "\n" ); } } @} \newpage \chapter{Implementa\c c\~{a}o \textsf{Lex/Yacc}} Neste caso, n\~{a}o se usa propriamente uma gram\'{a}tica de atributos, mas sim uma gram\'{a}tica tradutora que apenas tem atributos sintetizados (um por cada s\'{i}mbolo).\\ \section{Lex} A descri\c c\~{a}o l\'{e}xica \'{e} feita \`{a} frente da \texttt{Gramática Tradutora} no ficheiro "Lavanda.l" que ser\'{a} tratada pela ferramenta \textsf{Flex}.\\ @o Lavanda.l @{ %{ #include #include #include "y.tab.h" %} digit [0-9] day [0-3]?{digit} month [0-1]?{digit} year [0-2]{digit}{digit}{digit} sep [//|-] data {day}{sep}{month}{sep}{year} %% @} @o Lavanda.l @{ {digit}* { yylval.number=atoi(yytext); return NUMBER; } {data} { yylval.string=strdup(yytext); return DATA; } "alg" { yylval.string=strdup(yytext); return ALG; } "br" { yylval.string=strdup(yytext); return BR; } "cor" { yylval.string=strdup(yytext); return COR; } "casa" { yylval.string=strdup(yytext); return CASA; } "corpo" { yylval.string=strdup(yytext); return CORPO; } "fib" { yylval.string=strdup(yytext); return FIB; } "la" { yylval.string=strdup(yytext); return LA; } [a-z]+ { yylval.string=strdup(yytext); return IDENT; } [ \n\t\f] { /* white space is skipped */ } [-.;,()?] { return yytext[0]; } . ; %% int yywrap() { return 1; } @} \section{Yacc} Quanto \`{a} gram\'{a}tica tradutora, \'{e} escrita num \'{u}nico ficheiro --- Lavanda.y --- para ser processado pela ferramenta \textsf{Yacc}. O tipo do atributo sintetizado que se pode associar a cada s\'{i}mbolo e a respectiva associa\c c\~{a}o ser\'{a} feita no in\'{i}cio antes da gram\'{a}tica.\\ As ac\c c\~{o}es sem\^{a}nticas, agora especificadas em \textsf{C}, s\~{a}o escritas entre '\{' e '\}' ao longo de cada produ\c c\~{a}o (como em \textsf{CoCoR}).\\ As fun\c c\~{o}es auxiliares s\~{a}o escritas em \textsf{C} e s\~{a}o incluidas no mesmo ficheiro ap\'{o}s a gram\'{a}tica, tal como em \lisa.\\ @o Lavanda.y @{ %{ #include #include #include "hashtable.c" #include "list.c" HashTable nLotes[TABSIZE], tablePrice[TABSIZE]; List idSacos; float custoTotal = 0; %} %union { int number; char *string; } @} @o Lavanda.y @{ %token NUMBER %token IDENT DATA CORPO CASA COR BR LA FIB ALG %type Sacos Saco Lotes Outros %type Tipo Classe Tinto Fio %start Lavanda %% Lavanda: Cabec Sacos { printf("\nTabela Lotes:\n"); printHashTable(nLotes); printf("\nTotal Sacos: %d\n", $2); exit(0); } ; Cabec: DATA IDENT ; Sacos: Saco '.' { $$ = 1; } | Sacos Saco '.' { $$ = $1 + 1; } ; Saco: NUMBER IDENT '(' Lotes ')' { if (searchList(idSacos,$1)==0) { printf("Saco repetido!\n"); exit(0); } idSacos = insertList(idSacos,$1); printf("\nSaco n. %d tem --> %d lotes!\n", $1, $4); printf("Custo total do saco: %.2f.\n", custoTotal); custoTotal = 0; } ; Lotes: Lote Outros { $$ = $2 + 1; } ; Lote: Tipo NUMBER { updateTableNLotes(nLotes,$1,$2); custoTotal += searchHashTable(tablePrice,$1)*$2; } ; @} @o Lavanda.y @{ Outros: { $$ = 0; } | ';' Lotes { $$ = $2; } ; Tipo: Classe '-' Tinto '-' Fio { char *aux = strcat($1,"-"); aux = strcat(aux, $3); aux = strcat(aux,"-"); $$ = strcat(aux,$5); } ; Classe: CORPO { $$ = $1; } | CASA { $$ = $1; } ; Tinto: BR { $$ = $1; } | COR { $$ = $1; } ; Fio: ALG { $$ = $1; } | FIB { $$ = $1; } | LA { $$ = $1; } ; %% void initNLotes(HashTable env[]) { newHashTable(env); insertHashTable(env,"corpo-br-la",0); insertHashTable(env,"corpo-br-alg",0); insertHashTable(env,"corpo-br-fib",0); insertHashTable(env,"corpo-cor-la",0); insertHashTable(env,"corpo-cor-alg",0); insertHashTable(env,"corpo-cor-fib",0); insertHashTable(env,"casa-br-la",0); insertHashTable(env,"casa-br-alg",0); insertHashTable(env,"casa-br-fib",0); insertHashTable(env,"casa-cor-la",0); insertHashTable(env,"casa-cor-alg",0); insertHashTable(env,"casa-cor-fib",0); } @} @o Lavanda.y @{ void initTablePrice(HashTable env[]) { newHashTable(env); insertHashTable(env,"corpo-br-la",1.0); insertHashTable(env,"corpo-br-alg",2.2); insertHashTable(env,"corpo-br-fib",3.4); insertHashTable(env,"corpo-cor-la",4.5); insertHashTable(env,"corpo-cor-alg",3.7); insertHashTable(env,"corpo-cor-fib",1.9); insertHashTable(env,"casa-br-la",2.6); insertHashTable(env,"casa-br-alg",5.3); insertHashTable(env,"casa-br-fib",7.1); insertHashTable(env,"casa-cor-la",3.5); insertHashTable(env,"casa-cor-alg",2.5); insertHashTable(env,"casa-cor-fib",2.3); } int yyerror(char* error) { fprintf(stdout,"Error: %s\n", error); return 0; } int main() { initNLotes(nLotes); initTablePrice(tablePrice); idSacos = createList(); return yyparse(); } @} \chapter{Implementa\c c\~{a}o \textsf{LRC}} Neste caso a gram\'{a}tica de atributos \'{e} especificada em v\'{a}rios ficheiros, como se mostra nas sec\c c\~{o}es seguintes. H\'{a} mecanismos de separar a gram\'{a}tica sem\^{a}ntica da abstracta. Os atributos s\~{a}o declarados e associados aos s\'{i}mbolos em sec\c c\~{a}o pr\'{o}pria e as regras de c\'{a}lculo s\~{a}o associadas \`{a}s produ\c c\~{o}es, no fim, escrevendo-as entre '\{' e '\}' em linguagem pr\'{o}pria (\textsf{SSL}).\\ Devido \`{a} exist\^{e}ncia de v\'{a}rios ficheiros e \`{a} forma como devem ser compilados, incluimos numa \'{u}ltima sec\c c\~{a}o uma makefile para produzir e invocar o processador. \section{Context Free Grammar} @o CFG.ssl @{ /*********************************** Scanner ***********************************/ WHITESPACE : < [\ \t\n]+ >; DM : < [0-3][0-9] >; YEAR : < [0-2][0-9][0-9][0-9] >; NUMBER : < [0-9]+ >; CORPO : < "corpo" >; CASA : < "casa" >; COR : < "cor" >; BR : < "br" >; ALG : < "alg" >; FIB : < "fib" >; LA : < "la" >; IDENTIFIER : < [a-z]+ >; @} @o CFG.ssl @{ /******************************** Parser ********************************/ lavanda { syn Lavanda ast; }; lavanda ::= (cabec sacos ) { $$.ast = RootLavanda( sacos.ast ); } ; cabec { syn Cabec ast ; }; cabec ::= (data IDENTIFIER) { $$.ast = NoSyn(); } ; data { syn Cabec ast; }; data ::= (DM '-' DM '-' YEAR) { $$.ast = NoSyn(); }; sacos { syn Sacos ast ; } ; sacos ::= ( saco moreSacos ) { $$.ast = ConsSacos(saco.ast,moreSacos.ast); }; moreSacos { syn Sacos ast; }; moreSacos ::= () { $$.ast = NoSacos(); } | ( sacos ) { $$.ast = sacos.ast; }; saco { syn Saco ast ; } ; saco ::= (NUMBER IDENTIFIER '(' lotes ')' ) { $$.ast = ConsSaco(IDENTIFIER,STRtoINT(NUMBER),lotes.ast); } ; lotes { syn Lotes ast; }; lotes ::= ( lote outros ) { $$.ast = ConsLotes(lote.ast,outros.ast); }; lote { syn Lote ast; }; lote ::= ( tipo NUMBER ) { $$.ast = ConsLote(tipo.ast,STRtoINT(NUMBER)); }; outros { syn Lotes ast; }; outros ::= () { $$.ast = NoLotes(); } | ( ',' lotes ) { $$.ast = lotes.ast; }; @} @o CFG.ssl @{ tipo { syn Tipo ast; }; tipo ::= (classe '-' tinto '-' fio ) { $$.ast = ConsName(classe.ast,tinto.ast,fio.ast); }; classe { syn Name ast; }; classe ::= ( CORPO ) { $$.ast = ConsStr("corpo"); } | ( CASA ) { $$.ast = ConsStr("casa"); }; tinto { syn Name ast; }; tinto ::= ( COR ) { $$.ast = ConsStr("cor"); } | ( BR ) { $$.ast = ConsStr("br"); }; fio { syn Name ast; }; fio ::= ( FIB ) { $$.ast = ConsStr("fib"); } | ( LA ) { $$.ast = ConsStr("la"); } | ( ALG ) { $$.ast = ConsStr("alg"); }; @} \break \section{Abstract Syntax Tree} @o AST.ssl @{ Lavanda ~ lavanda.ast ; root Lavanda; Lavanda : RootLavanda (Sacos) ; Cabec : NoSyn () ; Sacos : NoSacos () | ConsSacos (Saco Sacos) ; Saco : NoSaco() | ConsSaco (STR INT Lotes) ; Lotes : NoLotes() | ConsLotes ( Lote Lotes) ; Lote : ConsLote ( Tipo INT) ; Tipo : ConsName(Name Name Name); Name : ConsStr(STR); @} \break \section{Semantics} @o Semantics.ssl @{ /*************** Attributes ***************/ /***** b) *****/ list tableLotes; tableLotes : Empty() | ConsTableLotes ( STR INT tableLotes); tableLotes: Empty [ @@ ::= "" ] | ConsTableLotes [ @@ ::= "\t" @@ "\t\t" @@ "\n" @@ ]; /***** c) *****/ list tablePrice; tablePrice: EmptyPrice() | ConsTablePrice( STR REAL tablePrice); tablePrice: EmptyPrice [ @@ ::= ""] | ConsTablePrice [ @@ ::= "\t" @@ "\t\t" @@ "\n\n" @@]; list idSacos; idSacos: EmptyIds() | ConsIdSacos (INT idSacos); idSacos: EmptyIds [ @@ ::= "" ] | ConsIdSacos [ @@ ::= "\t" @@ "\n\n" @@]; /******/ Lavanda { syn INT nSacos; syn tableLotes tLotesOut; } ; @} @o Semantics.ssl @{ Lavanda : RootLavanda { local STR numSacos; local tableLotes tLotes; numSacos = "\n\nNumero de sacos: " # INTtoSTR($$.nSacos) # "\n\n"; $$.nSacos = Sacos.nSacos; /* b) */ tLotes = $$.tLotesOut; Sacos.tLotesIn = initEnv(Empty()); $$.tLotesOut = Sacos.tLotesOut; /* c) */ Sacos.tPriceIn = initEnvPrice(EmptyPrice()); Sacos.inIds = EmptyIds(); }; Sacos { syn INT nSacos; /* b) */ inh tableLotes tLotesIn; syn tableLotes tLotesOut; /* c) */ inh tablePrice tPriceIn; inh idSacos inIds; syn idSacos outIds; }; Sacos : NoSacos { $$.nSacos = 0; $$.tLotesOut = $$.tLotesIn; /* c) */ $$.outIds = $$.inIds; } | ConsSacos { $$.nSacos = Sacos$2.nSacos + 1; /* b) */ Saco.tLotesIn = $$.tLotesIn; Sacos$2.tLotesIn = Saco.tLotesOut; $$.tLotesOut = Sacos$2.tLotesOut; /* c) */ Saco.tPriceIn = $$.tPriceIn; Sacos$2.tPriceIn = $$.tPriceIn; Saco.inIds = $$.inIds; Sacos$2.inIds = Saco.outIds; $$.outIds = Sacos$2.outIds; }; @} @o Semantics.ssl @{ Saco { syn INT nLotes; /* b) */ inh tableLotes tLotesIn; syn tableLotes tLotesOut; /* c) */ inh tablePrice tPriceIn; syn REAL custoTotal; inh idSacos inIds; syn idSacos outIds; }; Saco : NoSaco { /* b) */ $$.nLotes = 0; $$.tLotesOut = $$.tLotesIn; /* c) */ $$.custoTotal = 0.0; $$.outIds = $$.inIds; } | ConsSaco { local STR infLotesPrice; local STR erro; infLotesPrice = "\nNumero de lotes no saco n. " # INTtoSTR(INT) # ": " # INTtoSTR($$.nLotes) # "\nPreco total: " # REALtoSTR($$.custoTotal); $$.nLotes = Lotes.nLotes; /* b) */ Lotes.tLotesIn = $$.tLotesIn; $$.tLotesOut = Lotes.tLotesOut; /* c) */ Lotes.tPriceIn = $$.tPriceIn; $$.custoTotal = Lotes.custoTotal; $$.outIds = joinId($$.inIds,INT); erro = belongId($$.inIds,INT) ? "Erro: saco ja existente!" : ""; }; Lotes { syn INT nLotes; /* b) */ inh tableLotes tLotesIn; syn tableLotes tLotesOut; /* c) */ inh tablePrice tPriceIn; syn REAL custoTotal; }; @} @o Semantics.ssl @{ Lotes: NoLotes { /* a) */ $$.nLotes = 0; /* b) */ $$.tLotesOut = $$.tLotesIn; /* c) */ $$.custoTotal = 0.0; } | ConsLotes { $$.nLotes = Lotes$2.nLotes + 1; /* b) */ Lote.tLotesIn = $$.tLotesIn; Lotes$2.tLotesIn = Lote.tLotesOut; $$.tLotesOut = Lotes$2.tLotesOut; /* c) */ Lote.tPriceIn = $$.tPriceIn; Lotes$2.tPriceIn = $$.tPriceIn; $$.custoTotal = Lote.custoTotal + Lotes$2.custoTotal; }; Lote { /* b) */ inh tableLotes tLotesIn; syn tableLotes tLotesOut; /* c) */ inh tablePrice tPriceIn; syn REAL custoTotal; }; Lote: ConsLote { /* b) */ $$.tLotesOut = changeTableLotes($$.tLotesIn,Tipo.name,INT); /* c) */ $$.custoTotal = lookupPrice($$.tPriceIn,Tipo.name) * INTtoREAL(INT); }; @} @o Semantics.ssl @{ Tipo { syn STR name; }; Tipo : ConsName { $$.name = Ident(Name$1) # "-" # Ident(Name$2) # "-" # Ident(Name$3); }; /***** b) **********/ tableLotes initEnv(tableLotes table) { with(table) ( Empty() : ConsTableLotes("corpo-br-la",0, ConsTableLotes("corpo-br-fib",0, ConsTableLotes("corpo-br-alg",0, ConsTableLotes("corpo-cor-la",0, ConsTableLotes("corpo-cor-fib",0, ConsTableLotes("corpo-cor-alg",0, ConsTableLotes("casa-br-la",0, ConsTableLotes("casa-br-fib",0, ConsTableLotes("casa-br-alg",0, ConsTableLotes("casa-cor-la",0, ConsTableLotes("casa-cor-fib",0, ConsTableLotes("casa-cor-alg",0,Empty())))))))))))) , * : Empty() ) }; @} @o Semantics.ssl @{ tableLotes changeTableLotes(tableLotes table,STR name, INT num) { with(table) ( Empty() : Empty(), ConsTableLotes(name1,num1,tail) : (name1 == name) ? ConsTableLotes(name,num+num1,tail) : ConsTableLotes(name1, num1, changeTableLotes(tail,name,num)) ) }; STR Ident(Name s) { with(s) (ConsStr(s1): s1, * : "" ) }; /******** c) ********/ tablePrice initEnvPrice(tablePrice table) { with(table) ( EmptyPrice() : ConsTablePrice("corpo-br-la",1.0, ConsTablePrice("corpo-br-fib",2.2, ConsTablePrice("corpo-br-alg",3.4, ConsTablePrice("corpo-cor-la",4.5, ConsTablePrice("corpo-cor-fib",1.9, ConsTablePrice("corpo-cor-alg",3.7, ConsTablePrice("casa-br-la",2.6, ConsTablePrice("casa-br-fib",7.1, ConsTablePrice("casa-br-alg",5.3, ConsTablePrice("casa-cor-la",3.5, ConsTablePrice("casa-cor-fib",2.3, ConsTablePrice("casa-cor-alg",2.5,EmptyPrice())))))))))))) , * : EmptyPrice() ) }; @} @o Semantics.ssl @{ REAL lookupPrice(tablePrice table,STR name) { with(table) ( EmptyPrice() : 0.0, ConsTablePrice(name1,num1,tail) : (name1 == name) ? num1 : lookupPrice(tail,name) ) }; idSacos joinId(idSacos ids, INT numSaco) { with(ids) ( EmptyIds() : ConsIdSacos(numSaco,EmptyIds()), ConsIdSacos(num,tail) : ConsIdSacos(num,joinId(tail,numSaco)) ) }; BOOL belongId(idSacos ids, INT numSaco) { with(ids) ( EmptyIds() : false, ConsIdSacos(num,tail) : (num == numSaco) ? true : belongId(tail,numSaco) ) }; @} \break \section{Unparsing} @o Unparsing.ssl @{ Lavanda : RootLavanda [ @@ ::= @@ numSacos "Tabela Lotes:\n\t -Descricao- \t -N. Lotes-\n" tLotes] ; Cabec : NoSyn [ @@ ::= ""] ; Sacos : ConsSacos [ @@ ::= @@ " " @@ ] ; Saco : ConsSaco [ @@ ::= infLotesPrice " " erro] ; @} \section{Makefile} @o makefileLRC @{ RUNLRC=perl -S -w /usr/local/lrc/bin/runlrc.pl SSL_SOURCES = LavandaCFG.ssl attributes.ssl LRC_OPTIONS = -no_warnings -remove_deadends -v2c_code eva : code.ivr bcode.ieq $(RUNLRC) +4 -4 $(LRC_OPTIONS) bcode.ieq code.ivr : $(SSL_SOURCES) $(RUNLRC) -3 $(LRC_OPTIONS) $(SSL_SOURCES) clean : $(RUNLRC) -clean rm -f eva rm -f bcode.* rm -f code.* rm -f Save.* @} \newpage \chapter{Implementa\c c\~{a}o AntLR} Neste caso voltamos a ter toda a $GA$, incluindo a especifica\c c\~{a}o l\'{e}xica num \'{u}nico ficheiro --- Lavanda.g --- como se mostra a seguir. O aspecto geral \'{e} semelhante ao CoCoR, em que os atributos s\~{a}o declarados nas produ\c c\~{o}es associados com os respectivos s\'{i}mbolos entre '[' e ']' e as regras de c\'{a}lculo, escritas tamb\'{e}m em \textsf{C\#}, s\~{a}o delimitadas por '\{' e '\}' ao longo da produ\c c\~{a}o. As fun\c c\~{o}es auxiliares, em \textsf{C\#}, escrevem-se em m\'{o}dulos separados, incluidos na sec\c c\~{a}o \ref{table} e \ref{main}. \section{Gram\'{a}tica} @o Lavanda.g @{ header { // gets inserted in the C# source file before any // generated namespace declarations // hence -- can only be using directives using System.Collections; } options { language = "CSharp"; namespace = "Lavanda"; // encapsulate code in this namespace } class MyLexer extends Lexer; options { caseSensitive=false; // D = d (on LEXER rules match) caseSensitiveLiterals=false; // FOR = fOr (only for tokens) } @} @o LexerParser.g @{ COMMA : ',' ; LPAREN : '(' ; RPAREN : ')' ; MINUS : '-' ; protected LETTER : 'a'..'z'; protected DIGIT : '0'..'9'; IDENTIFIER : LETTER ( LETTER )* ; NUMBER : DIGIT ( DIGIT )* ; protected NEWLINE : ( options { generateAmbigWarnings=false; } : '\n' | '\r' | '\r''\n' ) { newline(); } ; WHITESPACE : ( ' ' | '\t' | NEWLINE )+ { $setType(Token.SKIP); } ; class MyParser extends Parser; options { //exportVocab=My; } { public OpHashtable opTable; } @} @o LexerParser.g @{ lavanda options {defaultErrorHandler=false;} { int nSacos = 0; Hashtable inTable = opTable.initNLotes(), outTable, inTPrice = opTable.initTablePrice(); ArrayList inIds = opTable.initIds(), outIds = inIds; } : cabec sacos[out nSacos, inTable, out outTable, inTPrice, inIds, out outIds] { Console.WriteLine("Total sacos: " + nSacos + "\n"); opTable.printTableLotes(outTable); } ; cabec : date IDENTIFIER ; date : NUMBER MINUS NUMBER MINUS NUMBER ; sacos [ out int nSacos , Hashtable inTable, out Hashtable outTable, Hashtable inTPrice, ArrayList inIds, out ArrayList outIds] options {defaultErrorHandler=false;} { int nSacos1 = 0; float custoTotal; } : saco[inTable, out outTable, inTPrice, out custoTotal, inIds, out outIds] { nSacos = 1;} ( sacos[out nSacos1, outTable, out outTable, inTPrice, outIds, out outIds] )* { nSacos += nSacos1;} ; @} @o LexerParser.g @{ saco [ Hashtable inTable, out Hashtable outTable, Hashtable inTPrice, out float custoTotal, ArrayList inIds, out ArrayList outIds] options {defaultErrorHandler=false;} { int nLotes = 0, num; custoTotal = 0; } : t1:NUMBER { num = Convert.ToInt32(t1.getText()); } IDENTIFIER LPAREN lotes[out nLotes, inTable, out outTable, inTPrice, out custoTotal] RPAREN { Console.WriteLine("N. lotes no saco " + num + ": " + nLotes); opTable.writePrice(custoTotal); if ( inIds.Contains( num )) throw new Exception("Cliente already exists!"); outIds = opTable.newId(inIds, num); } ; lotes [out int nLotes, Hashtable inTable, out Hashtable outTable, Hashtable inTPrice, out float custoTotal] options {defaultErrorHandler=false;} { int nLotes1 = 0; float custoTotal2 = 0; } : lote[inTable, out outTable, inTPrice, out custoTotal] {nLotes = 1;} ( COMMA lotes[out nLotes1, inTable, out outTable, inTPrice, out custoTotal2] )* {nLotes += nLotes1; outTable=inTable; custoTotal +=custoTotal2;} ; lote [Hashtable inTable, out Hashtable outTable, Hashtable inTPrice, out float custoTotal] options {defaultErrorHandler=false;} { string name = ""; } : tipo[out name] t1:NUMBER { outTable = opTable.updateTableLotes(inTable, name, Convert.ToInt32(t1.getText())); custoTotal = opTable.lookupPrice(inTPrice, name) * Convert.ToInt32(t1.getText()); } ; @} @o LexerParser.g @{ tipo [out string name] options {defaultErrorHandler=false;} { string name1 = "", name2 = "", name3 = ""; } : classe[out name1] MINUS tinto[out name2] MINUS fio[out name3] { name = name1 + "-" + name2 + "-" + name3; } ; classe [out string name] options {defaultErrorHandler=false;} { name = ""; } : "corpo" { name = "corpo"; } | "casa" { name = "casa"; } ; tinto [out string name] options {defaultErrorHandler=false;} { name = ""; } : "br" { name = "br"; } | "cor" { name = "cor"; } ; fio [out string name] options {defaultErrorHandler=false;} { name = ""; } : "alg" { name = "alg"; } | "la" { name = "la"; } | "fib" { name = "fib"; } ; @} \section{Fun\c c\~{o}es auxiliares} \label{table} @o Op_Hashtable.cs @{ using System; using System.Collections; namespace Lavanda { public class OpHashtable { public OpHashtable() { } // alinea b public Hashtable initNLotes() { Hashtable env = new Hashtable(); env.Add("corpo-br-la",0); env.Add("corpo-br-alg",0); env.Add("corpo-br-fib",0); env.Add("corpo-cor-la",0); env.Add("corpo-cor-alg",0); env.Add("corpo-cor-fib",0); env.Add("casa-br-la",0); env.Add("casa-br-alg",0); env.Add("casa-br-fib",0); env.Add("casa-cor-la",0); env.Add("casa-cor-alg",0); env.Add("casa-cor-fib",0); return env; } public Hashtable updateTableLotes(Hashtable inTable, String name, int number) { IDictionaryEnumerator env = inTable.GetEnumerator(); while(env.MoveNext()) { string key = (string)env.Key; if (key.Equals(name)) { int pieces = (int)env.Value + number; inTable.Remove(key); inTable.Add(key, pieces); break; } } return inTable; } @} @o Op_Hashtable.cs @{ public void printTableLotes( Hashtable myList ) { IDictionaryEnumerator myEnumerator = myList.GetEnumerator(); Console.WriteLine( "\t-Descricao-\t-N. Lotes-" ); while ( myEnumerator.MoveNext() ) Console.WriteLine("\t{0}:\t{1}", myEnumerator.Key, myEnumerator.Value); Console.WriteLine(); } // alinea c public Hashtable initTablePrice() { Hashtable env = new Hashtable(); env.Add("corpo-br-la",1.0f); env.Add("corpo-br-alg",2.2f); env.Add("corpo-br-fib",3.4f); env.Add("corpo-cor-la",4.5f); env.Add("corpo-cor-alg",3.7f); env.Add("corpo-cor-fib",1.9f); env.Add("casa-br-la",2.6f); env.Add("casa-br-alg",5.3f); env.Add("casa-br-fib",7.1f); env.Add("casa-cor-la",3.5f); env.Add("casa-cor-alg",2.5f); env.Add("casa-cor-fib",2.3f); return env; } public float lookupPrice (Hashtable inPrice, string name ) { float price = 0; IDictionaryEnumerator env = inPrice.GetEnumerator(); while ( env.MoveNext() ) { if (env.Key.Equals(name)) { price = (float)env.Value; break; } } return price; } @} @o Op_Hashtable.cs @{ public ArrayList initIds() { return ( new ArrayList() ); } public ArrayList newId(ArrayList old, int num) { old.Add(num); return ( (ArrayList)old.Clone() ); } public void writePrice(float num) { Console.WriteLine("Preco Total: " + num + "\n" ); } } } // end namespace @} \section{Main} \label{main} @o LavandaCompiler.cs @{ using System; using System.Collections; using System.IO; using antlr; using Lavanda; public class LavandaCompiler { public static void Main (string[] args) { if (args.Length > 0) { Stream stream = new FileStream(args[0], FileMode.Open, FileAccess.Read, FileShare.Read); MyLexer lexer = new MyLexer(stream); MyParser parser = new MyParser(lexer); parser.opTable = new OpHashtable(); parser.lavanda(); } else Console.WriteLine("-- No source file specified"); } } @} \newpage \chapter{Resultados execu\c c\~{a}o} Consideremos como teste o seguinte exemplo:\\ @o Teste.test @{ 10-11-2005 today 1 dani (corpo-cor-la 1 , casa-cor-alg 2) 2 pedro (casa-br-fib 4) 3 celina (corpo-cor-alg 2, corpo-cor-la 3, corpo-cor-fib 1, casa-cor-alg 2, casa-cor-la 3, casa-cor-fib 1) @} \section{Resultados obtidos no \textsf{LISA}} Antes de mostrarmos os resultados em termos de execu\c c\~{a}o do exemplo acima referido poderemos mostrar algumas das funcionalidades suportadas pelo \textsf{LISA} que contribui de forma significativa para a compreens\~{a}o da compila\c c\~{a}o de um programa.\\ Algumas dessas funcionalidades s\~{a}o:\\ \begin{enumerate} \item \texttt{Aut\'{o}mato:} O aut\'{o}mato produzido pelo \textsf{LISA} para a linguagem \emph{Lavanda} \'{e} o seguinte:\\ \centerline{\includegraphics[width=0.9\linewidth]{automato}} \item \texttt{BNF} \centerline{\includegraphics[width=1\linewidth]{bnf}} \item \texttt{Atributos herdados e sintetizados:} Vejamos, por exemplo, a \'{a}rvore dos atributos herdados e sintetizados para algumas das produ\c c\~{o}es da nossa \emph{Gabs}. Para a produ\c c\~{a}o: \begin{verbatim} p1a: Lavanda --> Cabec Sacos \end{verbatim} Os atributos herdados e sintetizados:\\ \centerline{\includegraphics[width=1\linewidth]{prod1}} Para a produ\c c\~{a}o: \begin{verbatim} p3a: Sacos --> Saco \end{verbatim} \centerline{\includegraphics[width=1\linewidth]{prod3}} \item \texttt{Firs/Follow} O \textsf{LISA} permite-nos ainda calcular o first e o follow das produ\c c\~{o}es da \emph{Gabs}. \centerline{\includegraphics[width=1\linewidth]{first_follow}} \end{enumerate} Assim, depois da an\'{a}lise destas funcionalidades poderemos ent\~{a}o mostrar o \emph{output} produzido pelo compilador: \centerline{\includegraphics[width=0.65\linewidth]{res_lisa}} \section{Resultados obtidos no \textsf{CoCoR}} Vejamos o resultado produzido pelo compilador para a linguagem \textbf{Lavanda} usando o \textsf{CoCoR}.\\ \centerline{\includegraphics[width=0.75\linewidth]{res_cocor}} \section{Resultados obtidos no \textsf{Lex/Yacc}} \begin{verbatim} [_localhost Lex-Yacc]# ./parser < test1.test Saco n. 1 tem --> 2 lotes! Custo total do saco: 9.50. Saco n. 2 tem --> 1 lotes! Custo total do saco: 28.40. Saco n. 3 tem --> 6 lotes! Custo total do saco: 40.60. Tabela Lotes: casa-br-la 0 casa-br-fib 0 casa-br-alg 0 casa-cor-la 3 corpo-br-la 0 casa-cor-fib 1 casa-cor-alg 4 corpo-br-fib 4 corpo-br-alg 0 corpo-cor-la 4 corpo-cor-fib 1 corpo-cor-alg 2 Total Sacos: 3 \end{verbatim} \break \section{Resultados obtidos no \textsf{LRC}} \begin{verbatim} [_localhost LavandaLRC]$ ./eva -O Teste.test entering output descriptor: attr='(null)' view='BASEVIEW' file='(null)' /**** processing file Teste.test ****/ Unparse decorated term Numero de lotes no saco n. 1: 2 Preco total: 9.5 Numero de lotes no saco n. 2: 1 Preco total: 28.4 Numero de lotes no saco n. 3: 6 Preco total: 40.6 Numero de sacos: 3 Tabela Lotes: -Descricao- -N. Lotes- corpo-br-la 0 corpo-br-fib 0 corpo-br-alg 0 corpo-cor-la 4 corpo-cor-fib 1 corpo-cor-alg 2 casa-br-la 0 casa-br-fib 4 casa-br-alg 0 casa-cor-la 3 casa-cor-fib 1 casa-cor-alg 4 \end{verbatim} \newpage \section*{Resultados obtidos no \textsf{AntLR}} \newpage \chapter{Ficheiros no Documento} @f \end{document}