Compiladores: Símbolos y ámbitos - LSUBlsub.org/comp/slides/s09.symbol.pdf · 2016-01-25 ·...

Post on 19-Apr-2020

5 views 0 download

Transcript of Compiladores: Símbolos y ámbitos - LSUBlsub.org/comp/slides/s09.symbol.pdf · 2016-01-25 ·...

1/25/16, 2:49 PMCompiladores: Símbolos y ámbitos - (c)2014 LSUB

Page 1 of 40http://127.0.0.1:3999/s09.symbol.slide#1

Compiladores: Símbolos y ámbitosFrancisco J BallesterosLSUB, URJC

1/25/16, 2:49 PMCompiladores: Símbolos y ámbitos - (c)2014 LSUB

Page 2 of 40http://127.0.0.1:3999/s09.symbol.slide#1

Símbolos y ámbitos

En el lenguaje podemos tener

vars: a: int;procedure foo(a: int, b:int ) a: int;{}

Y tenemos que

saber qué es cada nombre

gestionar el ámbito en el que vive

1/25/16, 2:49 PMCompiladores: Símbolos y ámbitos - (c)2014 LSUB

Page 3 of 40http://127.0.0.1:3999/s09.symbol.slide#1

Símbolos y ámbitos

Tenemos un ámbito global

con nombres globales definidos

incluyendo nombres de procedimientos, funciones y tipos

Y dentro otro para cada procedimiento

Y dentro otro para las variables

Y puede que otros, mas anidados si el lenguaje lo permite

Se comportan como una pila

1/25/16, 2:49 PMCompiladores: Símbolos y ámbitos - (c)2014 LSUB

Page 4 of 40http://127.0.0.1:3999/s09.symbol.slide#1

Ámbitos

Hacen falta al compilar para saber qué es cada nombre

Y hará falta gestionarlos en tiempo de ejecución

normalmente con la pila utilizada para ejecutar el programa

1/25/16, 2:49 PMCompiladores: Símbolos y ámbitos - (c)2014 LSUB

Page 5 of 40http://127.0.0.1:3999/s09.symbol.slide#1

Tabla de Símbolos

Tendremos una pila de tablas (una por ámbito)

Cada una es una hash del nombre a los atributos del símbolo

type Skind int

const ( Snone Skind = iota Skey // keyword Stype // type name Sconst // constant name Svar // variable or parameter name Sproc // procedure name Sfunc // function name)

type Sym struct { name string // symbol name kind Skind // keyword, constant, var name, ... id int // token id

file string // where declared line int // where declared}

1/25/16, 2:49 PMCompiladores: Símbolos y ámbitos - (c)2014 LSUB

Page 6 of 40http://127.0.0.1:3999/s09.symbol.slide#1

Tabla de Símbolos

Un buen punto de partida es

definir los keywords en una tabla

definir los tipos predefinidos en una tabla

igual para constantes, variables, procedimientos y funciones

Recorrerlos y definir símbolos para todos ellos

bltin := make(map[string]*Sym, 101)env = []map[string]*Sym{bltin}

for i := range keywords { bltin[keywords[i].name] = &keywords[i] keywords[i].kind = Skey keywords[i].file = "builtin"}...pushEnv() // top-level

1/25/16, 2:49 PMCompiladores: Símbolos y ámbitos - (c)2014 LSUB

Page 7 of 40http://127.0.0.1:3999/s09.symbol.slide#1

Tabla de Símbolos

El primer entorno (ámbito) en la pila tiene elementos predefinidos

No vamos a dejar redefinirlos en ningún programa

Nos ayuda a simplificar la gramática

Ahora el scanner tiene que mirar la tabla de símbolos

Devolver ID para nombres (predefinidos o no)

Devolver TYPEID para nombres de tipos (predefinidos o no)

Igual para literales con nombre (incluyendo por ej. True)

1/25/16, 2:49 PMCompiladores: Símbolos y ámbitos - (c)2014 LSUB

Page 8 of 40http://127.0.0.1:3999/s09.symbol.slide#1

Tabla de Símbolos

Utilizaremos estas funciones para abrir y cerrar ámbitos

func pushEnv() { env := map[string]*Sym{} envs = append(envs, env)}

func popEnv() { if len(envs) == 1 { panic("bug: attempt to pop builtin env") } envs = envs[:len(envs)-1]}

1/25/16, 2:49 PMCompiladores: Símbolos y ámbitos - (c)2014 LSUB

Page 9 of 40http://127.0.0.1:3999/s09.symbol.slide#1

Tabla de Símbolos

Utilizaremos estas funciones para buscar y definir símbolos

func getbuiltin(n string) *Sym { if s, ok := envs[0][n]; ok { return s } return nil}

func getsym(n string) *Sym { for i := len(envs)-1; i >= 0; i-- { if s, ok := envs[0][n]; ok { return s } } return nil}

func defsym(s *Sym) { envs[len(envs)-1][s.name] = s}

1/25/16, 2:49 PMCompiladores: Símbolos y ámbitos - (c)2014 LSUB

Page 10 of 40http://127.0.0.1:3999/s09.symbol.slide#1

Literales

Hay literales con nombre y valor de tipos distintos Vamos a dividir LIT entre BOOL, INT, ...

%token <ival> INT%token <bval> BOOL%token <rval> FLOAT%token <cval> CHAR%token <sval> STR%token <sym> ID TYPEID

Y a definir

%union { sym *Sym ival int rval float64 bval bool cval rune sval string}

1/25/16, 2:49 PMCompiladores: Símbolos y ámbitos - (c)2014 LSUB

Page 11 of 40http://127.0.0.1:3999/s09.symbol.slide#1

El punto de partida

tabla de símbolos inicial (src/lang/dat.go)

scanner (src/lang/lex.go)

parser (src/lang/lang2.y)

entrada de prueba (src/lang/f.p)

salida (src/lang/f.out)

1/25/16, 2:49 PMCompiladores: Símbolos y ámbitos - (c)2014 LSUB

Page 12 of 40http://127.0.0.1:3999/s09.symbol.slide#1

Antes de probar el parser

Necesitamos que los nombres de tipos definidos sean tokens TYPEID y no ID

Vamos a poner acciones para definir al menos los tipos y podremos probar el parser

1/25/16, 2:49 PMCompiladores: Símbolos y ámbitos - (c)2014 LSUB

Page 13 of 40http://127.0.0.1:3999/s09.symbol.slide#1

Definiciones

typedef : ID '=' type ';' { $1.kind = Stype $1.id = TYPEID defsym($1) } | TYPEID '=' type ';' ;

1/25/16, 2:49 PMCompiladores: Símbolos y ámbitos - (c)2014 LSUB

Page 14 of 40http://127.0.0.1:3999/s09.symbol.slide#1

Pruebas

func main() { os.Args[0] = "pick" flag.Usage = usage flag.Parse() initenv() args := flag.Args() if len(args) != 1 { usage() }

l, err := newFileLex(args[0]) if err != nil { Fatal("%s: %s", args[0], err) } debugLex = true yyParse(l) os.Exit(nerrors)}

1/25/16, 2:49 PMCompiladores: Símbolos y ámbitos - (c)2014 LSUB

Page 15 of 40http://127.0.0.1:3999/s09.symbol.slide#1

Pruebas

tabla de símbolos (src/lang/dat.go)

scanner (src/lang/lex.go)

parser (src/lang/lang3.y)

entrada de prueba (src/lang/f.p)

salida (src/lang/f.out2)

Normalmente habríamos hecho

1. el scanner 2. la tabla de símbolos 3. la gramática, poco a poco y probándola con entradas

1/25/16, 2:49 PMCompiladores: Símbolos y ámbitos - (c)2014 LSUB

Page 16 of 40http://127.0.0.1:3999/s09.symbol.slide#1

Producciones de error

Para tolerar algunos errores vamos a poner producciones de error en algunos puntos

Luego habrá que mejorarlas

parser con errores iniciales (src/lang/lang4.y)

tabla de símbolos con debug flag (src/lang/dat2.go)

scanner (src/lang/lex.go)

entrada de prueba con errores (src/lang/ferr.p)

salida con errores (src/lang/f.out3)

1/25/16, 2:49 PMCompiladores: Símbolos y ámbitos - (c)2014 LSUB

Page 17 of 40http://127.0.0.1:3999/s09.symbol.slide#1

Definiciones y ámbitos

Para procesar definiciones:

llamar a pushEnv al abrir cada ámbito

llamar a popEnv al cerrarlo

incluso si hay errores sintáticos!

llamar a defXXX() para definir los nuevos símbolos

1/25/16, 2:49 PMCompiladores: Símbolos y ámbitos - (c)2014 LSUB

Page 18 of 40http://127.0.0.1:3999/s09.symbol.slide#1

Definiciones y ámbitos

Al entrar...

prochdr : PROCEDURE ID '(' optparms ')' { defproc($2) pushEnv($2.name) } | PROCEDURE error { pushEnv("proc") Errorf("bad procedure header") } ;

1/25/16, 2:49 PMCompiladores: Símbolos y ámbitos - (c)2014 LSUB

Page 19 of 40http://127.0.0.1:3999/s09.symbol.slide#1

Definiciones y ámbitos

Al salir...

procdef : prochdr ';' { popEnv() } | prochdr optvars body { popEnv() } | prochdr error body { popEnv() }

1/25/16, 2:49 PMCompiladores: Símbolos y ámbitos - (c)2014 LSUB

Page 20 of 40http://127.0.0.1:3999/s09.symbol.slide#1

Definiciones y ámbitos

Constantes

constdef : ID '=' expr ';' { defconst($1) } | error ';' { Errorf("bad constant declaration") Errflag = 0 } ;

1/25/16, 2:49 PMCompiladores: Símbolos y ámbitos - (c)2014 LSUB

Page 21 of 40http://127.0.0.1:3999/s09.symbol.slide#1

Definiciones y ámbitos

Tipos

typedef : ID '=' type ';' { deftype($1) } | TYPEID '=' type ';' | error ';' { Errorf("bad type declaration") Errflag = 0 } ;

1/25/16, 2:49 PMCompiladores: Símbolos y ámbitos - (c)2014 LSUB

Page 22 of 40http://127.0.0.1:3999/s09.symbol.slide#1

Definiciones y ámbitos

Variables (locales y globales)

vardef : ID ':' TYPEID ';' { defvar($1) } | error ';' { Errorf("bad variable declaration") Errflag = 0 } ;

1/25/16, 2:49 PMCompiladores: Símbolos y ámbitos - (c)2014 LSUB

Page 23 of 40http://127.0.0.1:3999/s09.symbol.slide#1

Definiciones y ámbitos

Con nuevos auxiliares para definir símbolos concretos...

func deftype(s *Sym) { s.kind = Stype s.id = TYPEID defsym(s)}

func defvar(s *Sym) { s.kind = Svar s.id = ID defsym(s)}

func defconst(s *Sym) { s.kind = Sconst s.id = ID defsym(s)}...

1/25/16, 2:49 PMCompiladores: Símbolos y ámbitos - (c)2014 LSUB

Page 24 of 40http://127.0.0.1:3999/s09.symbol.slide#1

Definiciones y ámbitos

parser con (algunas) declaraciones (src/lang/lang5.y)

tabla de símbolos mejorada (src/lang/dat2.go)

scanner (src/lang/lex.go)

entrada de prueba (src/lang/ferr.p)

salida (src/lang/f.out4)

1/25/16, 2:49 PMCompiladores: Símbolos y ámbitos - (c)2014 LSUB

Page 25 of 40http://127.0.0.1:3999/s09.symbol.slide#1

AST

Hay que definir

los campos que tiene cada record

los parámetros de cada procedimiento/función

etc

Necesitamos construir trozos del árbol de parsing

En realidad, un árbol sintáctico abstracto (AST) decorado

Y utilizar luego esos árboles para el proceso

1/25/16, 2:49 PMCompiladores: Símbolos y ámbitos - (c)2014 LSUB

Page 26 of 40http://127.0.0.1:3999/s09.symbol.slide#1

AST: Expresiones

Para cada nodo de interés vamos a utilizar

type Nd struct { op int sym *Sym ival int rval float64 sval string bval bool cval rune args []*Nd file string // where declared line int // where declared}

1/25/16, 2:49 PMCompiladores: Símbolos y ámbitos - (c)2014 LSUB

Page 27 of 40http://127.0.0.1:3999/s09.symbol.slide#1

AST: Expresiones

Y declaramos tipos para los no terminales

%union { sym *Sym ival int rval float64 bval bool cval rune sval string nd *Nd lst []*Nd}

%type <nd> lvalue literal primary expr caseexpr forcond parm field fieldcase%type <lst> args optargs parms optparms fields optfields ids fieldcases%type <bval> optref

1/25/16, 2:49 PMCompiladores: Símbolos y ámbitos - (c)2014 LSUB

Page 28 of 40http://127.0.0.1:3999/s09.symbol.slide#1

AST: Expresiones

Ahora hay que construir los nodos

lvalue : ID { $$ = newnd(ID, $1) } | lvalue '[' expr ']' { $$ = newnd('[', nil, $1, $3) } | lvalue '.' ID { nd := newnd(ID, $3) $$ = newnd('.', nil, $1, nd) } | lvalue '^' { $$ = newnd('^', nil, $1) } ;

1/25/16, 2:49 PMCompiladores: Símbolos y ámbitos - (c)2014 LSUB

Page 29 of 40http://127.0.0.1:3999/s09.symbol.slide#1

AST: Expresiones

Intentando aproximar su fichero y línea

func newnd(op int, sym *Sym, args ...*Nd) *Nd { nd := &Nd{op: op, sym: sym, args: args} if sym != nil { nd.file = sym.file nd.line = sym.line } else if len(args) > 1 { nd.file = args[0].file nd.line = args[0].line } else { nd.file = file nd.line = line } return nd}

1/25/16, 2:49 PMCompiladores: Símbolos y ámbitos - (c)2014 LSUB

Page 30 of 40http://127.0.0.1:3999/s09.symbol.slide#1

AST: Expresiones

Y las listas

optargs : args | { $$ = nil } ;

args : args ',' expr { $$ = append($1, $3) } | expr { $$ = []*Nd{$1} } ;

1/25/16, 2:49 PMCompiladores: Símbolos y ámbitos - (c)2014 LSUB

Page 31 of 40http://127.0.0.1:3999/s09.symbol.slide#1

AST: Expresiones

Para los símbolos resulta interesante incluir

un nodo como valor

un símbolo para el tipo (para declaraciones, por ej.)

type Sym struct { name string // symbol name kind Skind // keyword, constant, var name, ... id int // token id

val *Nd tnd *Sym

file string // where declared line int // where declared

}

1/25/16, 2:49 PMCompiladores: Símbolos y ámbitos - (c)2014 LSUB

Page 32 of 40http://127.0.0.1:3999/s09.symbol.slide#1

AST: Expresiones

Continuamos y hacemos lo mismo con todo lo necesario

Incluyendo un nuevo flag para volcar nodos

Así podemos ver el AST como en

const = const<Maxword>{ val int<30>{ }}

type = type<Tindchar>{ val range<int>{ sym type<int> arg int<1>{ } arg id<Maxword>{ sym const<Maxword> } }}

1/25/16, 2:49 PMCompiladores: Símbolos y ámbitos - (c)2014 LSUB

Page 33 of 40http://127.0.0.1:3999/s09.symbol.slide#1

AST: tokens falsos

Como hemos utilizado los tokens como tipo para los nodos

Tenemos que definir algunos tokens falsos

la alternativa es un nuevo enumerado

con nuevas funciones de depuración

pero muchos de los tokens tienen nodos

%token CALL PARMS PARM%token ORD RANGE

1/25/16, 2:49 PMCompiladores: Símbolos y ámbitos - (c)2014 LSUB

Page 34 of 40http://127.0.0.1:3999/s09.symbol.slide#1

AST

Esto nos deja este resultado

parser con nodos del AST (src/lang/lang7.y)

nueva tabla de símbolos (src/lang/dat5.go)

AST (src/lang/nd.go)

lex con strings to tokens para nodos (src/lang/lex2.go)

entrada de prueba (src/lang/f2.p)

salida (src/lang/f.out5)

1/25/16, 2:49 PMCompiladores: Símbolos y ámbitos - (c)2014 LSUB

Page 35 of 40http://127.0.0.1:3999/s09.symbol.slide#1

AST Cntd.

Tendremos que hacer lo mismo para generar código con las sentencias y estructuras de control.

podemos aumentar los tipos de nodos

o definir otro tipo de árbol para sentencias

1/25/16, 2:49 PMCompiladores: Símbolos y ámbitos - (c)2014 LSUB

Page 36 of 40http://127.0.0.1:3999/s09.symbol.slide#1

AST Cntd.

Por ejemplo

stmt : lvalue '=' expr ';' { $$ = newnd('=', nil, $1, $3) } | ID '(' optargs ')' ';' { $$ = newnd(CALL, $1, $3...) } | body ...

1/25/16, 2:49 PMCompiladores: Símbolos y ámbitos - (c)2014 LSUB

Page 37 of 40http://127.0.0.1:3999/s09.symbol.slide#1

AST Cntd.

Y tendremos para

{ writeln(nc(max));}

este AST

main body = {}{ arg call<writeln>{ sym proc<writeln> arg call<nc>{ sym func<nc> arg id<max>{ sym var<max> } } }}

1/25/16, 2:49 PMCompiladores: Símbolos y ámbitos - (c)2014 LSUB

Page 38 of 40http://127.0.0.1:3999/s09.symbol.slide#1

Fuente con el AST

parser con nodos del AST (src/lang/lang8.y)

tabla de símbolos (src/lang/dat5.go)

AST (src/lang/nd.go)

scanner (src/lang/lex2.go)

entrada de prueba (src/lang/f.p)

salida (src/lang/f.out6)

1/25/16, 2:49 PMCompiladores: Símbolos y ámbitos - (c)2014 LSUB

Page 39 of 40http://127.0.0.1:3999/s09.symbol.slide#1

¿Ahora qué?

Falta procesar parámetros en los procedimientos

hay que definir un símbolo para cada uno en su ámbito

Hay que modificar el acceso a campos en records

buscando en el símbolo del tipo si está o no definido

Hay que evaluar expresiones evaluables en compilación

Hay que comprobar

tipos de datos

variables usadas y no asignadas y asignadas y no usadas

Hay que reescribir-optimizar partes del AST

Un switch pueden ser varios if

Hay que generar código

1/25/16, 2:49 PMCompiladores: Símbolos y ámbitos - (c)2014 LSUB

Page 40 of 40http://127.0.0.1:3999/s09.symbol.slide#1

Questions?

Francisco J BallesterosLSUB, URJChttp://lsub.org (http://lsub.org)