Você está na página 1de 14

30/04/2012

Linguagens e Programao

7 Tabela de Smbolos e Verificao de Tipos


Fontes: 1. Compiladores Princpios e prticas, Keneth C.Louden, Thomson, 2004. Cap. 6 Anlise Semntica 2. Compiladores Princpios,Tcnicas e Ferramentas AlfredV.Aho,R.Sethi e Jeffrey D.Ullman,2007. Cap. 6 Verificao de Tipos

Ana Madureira
Engenharia Informtica Ano Lectivo: 2011/2012

Tabela de Smbolos e Tipos: Funo


As tabelas de smbolos so extensamente utilizadas nos compiladores para armazenarem os nomes de variveis, tipos, classes, e outras estruturas de programao Alm do nome, cada entrada contm outras informaes acerca do objecto nomeado, como seja o seu tipo, endereo (offset no segmento de dados esttico, ou no registo de activao), nmero de referncias, etc. Geralmente todos os mdulos de um compilador necessitam de aceder tabela de smbolos, para criar novas entradas, adicionar informao quando esta descoberta (p. ex. o tipo de uma varivel), ou simplesmente consultar Alm disso, nas linguagens com estrutura de blocos, as tabelas de smbolos tero de reflectir o scope (mbito) dos identificadores que contm

30/04/2012

Tabela de Smbolos e Tipos: Tipos de ocorrncias


Ocorrncias definidores de identificadores: s suas possveis presenas em declaraes
Exemplo: VAR A:REAL; I:ARRAY[1...10] OF INTEGER

Ocorrncias aplicadas: s presenas em instrues


Exemplo: A:=10.17 A:=A+ I[5]

Tabela de Smbolos e Tipos


Ocorrncias definidores de identificadores e Ocorrncias aplicadas: A aco num compilador num caso e no outro diferente 1. Insero na tabela de smbolos no caso das ocorrncias definidores de identificadores 2. Pesquisa na tabela de smbolos (obteno do tipo......) no caso das ocorrncias aplicadas

30/04/2012

Tabelas de smbolos e tipos com um nico nvel


Nas linguagens onde existem apenas variveis globais no necessrio tomar nota do scope das mesmas (h apenas um nvel de acesso s variveis) Pode ser o prprio analisador lxico a criar as entradas na tabela de smbolos A tabela de smbolos ento uma estrutura de dados do tipo dicionrio simples, devendo suportar operaes como: add_symbol(x) - cria uma nova entrada na tabela com nome x lookup_symbol(x) - retorna a posio (apontador ou ndice) na tabela da entrada contendo o nome x, ou a indicao de que no existe delete_symbol(x) - retira o nome x da tabela. Estas tabelas podem ser implementadas como tabelas de hashing, rvores de pesquisa, listas ligadas, arrays, stacks, etc, com eficincia variada Nas linguagens com vrios espaos de nomes (para variveis, nomes de procedimentos, nomes de tipos, nomes de campos de estruturas, etc), poder ser necessrio dispor de tabelas separadas para cada espao de nomes

Tabelas multinvel
Nas linguagens estruturadas por blocos necessrio tomar nota de alguma forma do scope dos nomes Geralmente a prpria estrutura da tabela reflecte o embutimento dos blocos As operaes para este tipo de tabela incluem: create_scope(parent_scope) - cria um novo nvel, embutido no parent_scope, retornando um seu apontador insert(scope, x) - coloca o nome x no nvel scope (ocorrncias definidoras) lookup(x) - procura o nome x, comeando no scope mais prximo, retornando a sua posio na tabela (ocorrncias aplicadas) lookup_last(x) - procura apenas no nvel mais recente delete_scope(scope) - retira da tabela todo o nvel correspondente ao scope scope

30/04/2012

Verificao de tipos (anlise semntica)

Verificao de tipos
O compilador deve verificar: Tipos de dados dos operandos, fornecidos a um operador, e a respectiva definio do operador so legais e compatveis; isto deve, verificar se esto de acordo com o sistema de tipos definido na linguagem

O sistema de tipos define para cada operador da linguagem e/ou construo gramatical, os tipos dos operandos e os tipos dos resultados possveis As linguagens, para as quais todos os erros relacionados com o sistema de tipos podem ser detectados e identificados durante a compilao, dizem-se estaticamente tipadas (ou fortemente tipadas) As linguagens, para as quais pode ser necessrio, durante a execuo dos programas, fazer verificaes de tipos, a fim de detectar erros, dizem-se dinamicamente tipadas (Prolog, Lisp, Scheme, p.ex.)

30/04/2012

Sistemas de tipos
Um sistema de tipos uma coleco de regras para associar expresses de tipos s vrias partes de um programa Os sistemas de tipos podem ser especificados atravs de definies dirigidas pela sintaxe, ou seja com atributos e aces semnticas As regras definidas num sistema de tipos, so regras que podem ser lidas em linguagem corrente como: Se ambos os operandos dos operadores aritmticos de adio, subtraco e multiplicao forem do tipo inteiro, ento o resultado dessa operao do tipo inteiro O resultado do operador unrio '&' o endereo do seu operando; se o tipo do operando for " ", o tipo do resultado "apontador para " A expresso "apontador para " uma expresso de tipos (operao sobre tipos)

Converso entre tipos


Muitas linguagens de programao definem regras para a converso automtica de um tipo de um operando, de modo a torn-lo compatvel com o seu contexto Estas converses automticas so efectuadas pelo compilador, dizendo-se que teve lugar uma coero de tipos Geralmente numa expresso como "x + i", em que x do tipo 'real' e i do tipo 'inteiro', a varivel i sofre uma coero para real antes de se efectuar a adio

30/04/2012

Converso entre tipos


Muitas linguagens de programao definem casos nos quais o programador pode explicitamente converter o tipo de dados de uma expresso num outro, utilizando operadores especiais de converso (operadores de cast). Estes operadores so estaticamente tipados, uma vez que possuem tipos de entrada e sada bem definidos susceptveis de serem verificados pelo compilador Exemplo em C: p = (table *) calloc(n, sizeof(table));

O operador (table *) um cast.

Expresses de tipos
Uma expresso de tipos qualquer expresso cujo resultado um tipo: pode ser um tipo bsico, ou o resultado da aplicao de um operador designado por construtor de tipo, a qualquer outra expresso de tipos. Os tipos bsicos e construtores dependem da linguagem. Tipos bsicos: so normais os seguintes - boolean, char, integer, real, void; um tipo bsico especial o tipo 'type_error', utilizado para marcar situaes de erro durante a verificao de tipos Certas linguagens permitem associar um nome (identificador) a tipos definidos pelo utilizador; para essas linguagens os nomes que designam tipos so tambm expresses de tipos Um construtor de tipo aplicado a uma expresso de tipos uma expresso de tipos

30/04/2012

Construtores de tipos
Arrays - Seja T uma expresso de tipos; ento array(I, T) uma expresso que representa o tipo de um array com elementos do tipo T e conjunto de ndices I; geralmente I um subconjunto (subrange) dos inteiros Subrange Seja T um tipo simples ordenado; ento subrange(min, max, T) uma expresso que representa o conjunto dos valores t: {t T e min t max} Registos - Este construtor associa nomes e tipos dos campos de um registo ou estrutura Exemplo: record((address integer) (lexeme array(1..15, char))) define um tipo registo com 2 campos (address e lexeme) do tipo integer e array de char (com posies de 1 a 15)

Construtores de tipos
Apontadores - A expresso (de tipos) apontador(T), em que T tambm uma expresso, designa um tipo que um apontador para um objecto de tipo T Funes - Associa-se tambm um tipo s funes. O respectivo construtor normalmente notado por . O tipo de funo escreve-se D C, em que D o tipo do domnio da funo e C o do contradomnio (ou resultado); quando os parmetros de entrada so mais do que um, o tipo do domnio representado por um produto cartesiano Exemplo: A seguinte funo em Pascal function f (a, b : char) : integer; tem tipo: char char pointer(integer) Variveis - Em certas situaes pode haver necessidade de ter, em expresses de tipos, variveis que representam tipos

30/04/2012

Implementao da verificao de tipos


A verificao de tipos faz-se normalmente atravs de regras semnticas, que devem implementar o sistema de tipos da linguagem, e de um atributo (type) associado aos no-terminais da gramtica que contm o resultado de uma expresso de tipos Em muitos casos esse atributo pode ser sintetizado, o que torna muito fcil executar a verificao de tipos juntamente com a anlise sintctica Em muitas linguagens os identificadores devem ser declarados antes da sua utilizao e nessas declaraes indicam-se os respectivos tipos. Noutros casos os tipos so inferidos da forma como o identificador escrito (Fortran, Basic)

Uma linguagem simples e a sua verificao de tipos


A verificao de tipos para uma linguagem simples com a seguinte gramtica:
P D;S D D;D| id : T T char | integer | boolean | array [ num ] of T | T S id = E | if E then S | while E do S | S;S E char_const | num | bool_const | id | E mod E | E[E]| E<E| E and E | E

Esta linguagem produz programas compostos por declaraes (D), seguidas de instrues (S) As declaraes associam tipos (T) a identificadores; possui 3 tipos bsicos (char, integer e boolean), alm de apontadores e arrays. Os arrays so declarados com um nico valor de ndice, assumindo-se que este vai de 1 at esse valor; assim a declarao: array[256] of char corresponde a um array de 256 caracteres com ndices que vo de 1 a 256. No sistema de tipos a implementar usam-se ainda os nomes simples type_error, usado para assinalar erros e o valor void, associado s instrues sem erro. Como na 1 produo o D aparece antes de S, todas as declaraes devero estar feitas antes das instrues. Um programa gerado por esta gramtica poder ser: key : integer; result : boolean; result = key < 1999

30/04/2012

Aces semnticas para as declaraes


Durante as declaraes vo-se tomando nota dos tipos dos identificadores que nelas aparecem. Associado a todos os compiladores existe sempre uma tabela de smbolos, onde colocado o nome de todos os identificadores definidos pelo programador. Poder caber ao analisador lxico a tarefa de ir preenchendo a tabela de smbolos com os identificadores que vai descobrindo, retornando como atributo um apontador ou ndice do local onde o identificador ficou armazenado na tabela. Sempre que se descobre nova informao sobre um identificador, essa informao deve ser adicionada respectiva entrada na tabela de smbolos(ocorrncia definidora de identificadores). D id : T Vamos associar a cada no-terminal um atributo type para tomar nota do respectivo tipo. Como se disse os identificadores vm do analisador lxico com um atributo (entry) que permite o acesso sua entrada na tabela de smbolos. A funo addtype( ), usada nas aces semnticas, preenche um campo de uma entrada da tabela de smbolos onde dever estar o respectivo tipo. O terminal num vem do analisador lxico com o atributo val, que contm o seu valor.

{ addtype(id.entry, T.type) } T char { T.type = char } T integer { T.type = integer } T boolean { T.type = bool } T array [ num ] of T1 { T.type = array(1..num.val, T1.type) } T T1 { T.type = pointer(T1.type) }

Verificao dos tipos de expresses


Durante as declaraes apenas identificmos os tipos das variveis. Podiam--se a j identificar alguns erros, como as declaraes mltiplas. Por exemplo, se na tabela de smbolos j existir um tipo associado ao identificador, a funo addtype( ) poder identificar esse erro. A tabela de smbolos tambm guarda informao acerca do mbito dos identificadores. nas expresses que mais se faz sentir a necessidade da verificao de tipos. No esquema aqui apresentado faz-se uso de uma funo lookup( ) que retorna o tipo associado a um identificador, presente na tabela de smbolos. Quando o atributo type ganha o valor type_error, poder-se- a emitir uma mensagem de erro. Os terminais (tokens) podero trazer do analisador lxico informao acerca da sua posio no texto fonte, que poder ser aproveitada para a emisso das mensagens de erro.
E char_const E num E bool_const E id E E1 mod E2 { E.type = char } { E.type = integer } { E.type = bool } { E.type = lookup(id.entry) } { E.type = (E1.type == integer && E2.type == integer) ? integer : type_error } { E.type = (E2.type == integer && E1.type == array(s, t) ) ? t : type_error } { E.type = (E1.type == char && E2.type == char) ? bool : ((E1.type == integer && E2.type == integer) ? bool : type_error ) } { E.type = (E1.type == bool && E2.type == bool) ? bool : type_error } { E.type = (E1.type == pointer(t)) ? t : type_error }

E E1 [ E2 ]

E E1 < E2

E E1 and E2

E E1

30/04/2012

Verificao das instrues


Neste exemplo as instrues devero ter o tipo void (no existente na linguagem) ou type_error se os tipos dos seus constituintes no estiverem correctos. A 1 instruo apenas permite do lado esquerdo de uma atribuio um identificador; no entanto se a sua regra gramatical fosse S E = E, permitindo do lado esquerdo expresses como a[10] ou p, teramos de verificar, alm da compatibilidade de tipos, a existncia de um l-value para essa expresso. Por exemplo, poderamos ter um outro atributo (has_l-value) que seria construdo durante a anlise sintctica das expresses. L-value ou left-value representa directamente uma posio de memria. Algumas expresses que possuem l-value so: id, id[E], id (podem ser usadas no lado esquerdo de uma atribuio). R-value ou right-value representa o valor de uma expresso (o contedo de uma posio de memria ou o resultado de uma operao); pode ser usado do lado direito de uma atribuio.

S id = E S if E then S1 S while E do S1 S S1 ; S2

{ S.type = (E.type == lookup(id.entry)) ? void : type_error } { S.type = (E.type == bool) ? S1.type : type_error } { S.type = (E.type == bool) ? S1.type : type_error } { S.type = (S1.type == void && S2.type == void) ? void : type_error }

Verificao de funes
Embora a gramtica que nos serviu de exemplo at ao momento no inclua funes, vamos acrescent-las agora, considerando somente funes de um parmetro. A sua declarao (associao de um nome ao tipo de funo) pode ser feita acrescentando a seguinte regra para tipo: T T1 T2 A seta que aparece entre plicas um terminal da linguagem; uma declarao de uma funo, com aquela regra poderia ser: func : char integer que nos diz que func uma funo de argumento de tipo char com resultado de tipo inteiro. Para permitir a chamada de funes teramos agora de acrescentar mais uma regra s expresses: E E1 (E2) As regras semnticas de verificao de tipos poderiam ser:
T T1 T2 E E1 (E2) { T.type = T1.type T2.type } { E.type = (E2.type == s && E1.type == s t) ? t : type_error }

10

30/04/2012

Overloading de operadores
Diz-se que um operador est overloaded se tiver significados diferentes, dependentes do contexto Por exemplo, quando se usa um operador aritmtico, tal como +, normalmente este pode ser aplicado a inteiros, reais ou ainda a outros tipos, sendo assim necessrio verificar em que contexto aplicado para que se possa reconhecer o seu significado e, possivelmente, proceder converso de tipo de algum dos operandos : com A e B inteiros com A e B reais com A e B complexos Um operador overloaded, em cada situao, s poder ter um nico significado; no so permitidas ambiguidades A resoluo de uma situao de overloading designa-se por identificao de operadores A+ B

Resoluo de situaes de overloading


Uma situao ambgua de overloading fica resolvida quando possvel determinar um nico significado concreto da ocorrncia de um smbolo overloaded Muitas vezes a situao de overloading resolve-se apenas pelo exame dos tipos dos operandos Exemplo:

E E1 op E2 O tipo de E1 e E2 pode ser suficiente para determinar o significado do operador op.

Nestes casos os esquemas de verificao de tipos j abordados anteriormente servem para a resoluo da situao de overloading presente

11

30/04/2012

Verificao de tipos com overloading


Outras vezes no suficiente examinar apenas os tipos dos operandos. O tipo de uma expresso, em vez de poder ter um nico tipo, poder ter vrios possveis. Na maior parte das linguagens, o contexto deve providenciar informao suficiente para restringir a um nico tipo. Exemplo: Suponhamos que o operador * pode ter os seguintes trs significados:
1) integer integer integer 2) integer integer complex 3) complex complex complex

A:= 2 * (3 * 5) (1),(2) (1)

(3 * 5) * z (2) (3)

onde z complexo e 2, 3 e 5 so inteiros

O tipo do operador * na expresso com dois operandos inteiros no pode ser determinado conhecendo apenas os operandos

Exemplo (cont.)
Aps as declaraes anteriores, a expresso 3 * 5 tanto pode ser do tipo integer como do tipo complex, dependendo do seu contexto. Se a expresso completa 2 * (3 * 5), ento 3 * 5 ser do tipo integer. Mas se a expresso for (3 * 5) * z e se z for declarada como complex, ento 3 * 5 ser do tipo complex Soluo possvel: propagao dos tipos na rvore de parse, usando a notao ps-fixa e uma ordem de visita dos ns primeiro em profundidade

Expresso 3 * 5

Expresso (3 * 5) * z

12

30/04/2012

Polimorfismo
Funes polimrficas funes em que as instrues do seu corpo podem ser executadas com argumentos de diferentes tipos Funes polimrficas facilitam a implementao de algoritmos que manipulem estruturas de dados, independentemente do seu tipo A implementao de um mdulo de verificao de tipos pode ser dificultado no caso de linguagens com funes polimrficas Exemplo:

int max(int, int) - extrai o mximo entre dois inteiros int max(const int *, int) - extrai o mximo de um array de inteiros

Polimorfismo
Consideremos o seguinte pseudo-cdigo: A funo deref tem o mesmo efeito que os operadores do function deref(p); Pascal e * do C. begin p (do tipo ) um apontador para um objecto de tipo return p desconhecido . = pointer() end;

,point er( )
deref
Vamos usar uma linguagem para verificar funes polimrficas, gerada a partir da seguinte gramtica: P D; E D D; D | id : Q Q type_variable, Q | T T T T | basic_type | type_variable | (T) E E(E) | E, E | id

13

30/04/2012

Lb1 : Lb2 Lb1 subexpresso Polimorfismo Lb2 tipo da subexpresso Os programas gerados por esta gramtica so constitudos por uma sequncia de declaraes, seguido pela expresso E que se pretende verificar, por exemplo: deref: ,point er() deref q : pointer(pointer(integer)); deref(deref(q)) A rvore de parse para a expresso derefo(derefi(q)), cujo tipo se pretende verificar a seguinte:

aplicao : o derefo : pointer(o)o

pointer(o)=pointer(integer) aplicao : i

o=integer

pointer(i)=pointer(pointer(integer)) derefi : pointer(i)i

i=pointer(integer)

q : pointer(pointer(integer))

Cada aplicao da funo deref retira um nvel de referncia

14

Você também pode gostar