26. (cont.) S Id := E E + E id E + E id id S Id := E E + E E + E id Id id
27. (cont.) E E - E E - E 3 1 2 E E - E 1 E - E 2 3
28.
29.
30.
31.
32.
33.
34.
35. Programa del Parser Final int if = 1, then = 2, else = 3, begin = 4, end = 5, print = 6, semi = 7, num = 8, EQ = 9 int tok = get token ( ); void advance ( ) { tok = get token ( ); } void eat ( int t) { if ( tok == 1) advance ( ); else error ( ); } void S ( ) { switch ( tok ) { case If: eat ( if ); E ( ); eat ( then ); S ( ); eat ( else ); S ( ); break; case begin: eat ( begin ); S ( ); L ( ); break; case print: eat ( print ); E ( ); break; default: error; }} void L ( ) { switch ( tok ) { case end: eat ( end ); break; case semi: eat ( semi ); S ( ); L ( ); break; default: error ( ); }} void E ( ) { eat ( num ); eat ( EQ ); eat ( num ); }
36.
37.
38.
39.
40.
41.
42.
43. Algoritmo de Parsing LR (aho,Sethi y Ullman) Tomar el primer token de w$ /* w es la cadena */ Repeat forever begin Sea s el estado en el tope de la pila y a el token actual; if acción[s,a] = shift s’ then begin push a’ primero y sedpués s’ al tope de la pila; obten el siguiente token de la cadena de entrada else if acción[s,a] = reduce A->B then begin pop 2*|B| símbolos fuera de la pila; Sea s’ ahora el estado en el tope de la pila; Push A y después goto[s’,A] al tope de la pila; Imprime la producción A->B end else if acción[s,a] = accept then return else error() end
50. Ejemplo: void S ( ) { switch ( tok ) { case If: eat ( if ); E ( ); eat ( then ); S ( ); eat ( else ); S ( ); break; case begin: eat ( begin ); S ( ); L ( ); break; case print: eat ( print ); E ( ); break; default: print(“se esperaba if, begin o print”); }} Un problema que puede ocurrir al insertar un token faltante es que el programa caiga en un ciclo infinito, por eso a veces es preferible y mas seguro borrar el token, ya que el ciclo terminará cuando el EOF sea encontrado. Esta técnica trabaja muy cercanamente con la tabla de parsing (cuando el parser se implementa con tablas). En un parser del tipo LR o LALR, la tabla de parsing tiene 4 acciones: shift, reduce, accept y error (entrada nula). Cuando el parser encuentra una acción error , se para el proceso de análisis y se reporta la falla.
51.
52. Ejemplo: gramática para estatutos “begin”, “if” y “print” PARSER_BEGIN(MiniParser) public class MiniParser { public static void main(String[] args) { MiniParser parser; try { // RGC: added line if( args.length == 0 ) parser = new MiniParser(System.in); // RGC: added lines else parser= new MiniParser ( new java.io.FileInputStream( args[0] ) ); } // RGC: End parser.Program(); } catch (ParseException e) { System.out.println(e.getMessage()); } //RGC: added lines catch( Exception e ) { System.out.println(e.getMessage()); } //RGC :End } } PARSER_END(MiniParser) SKIP : { " " | "" | "" | "" }
61. (cont.) S E$ E T E’ E’ + T E’ E’ - T E’ E’ T F T’ T’ * F T’ T’ / F T’ T’ F id F num F (E) Los tokens ID y NUM deben ahora acarrear valores de tipo string e int , respectivamente. Asumiremos que existe una tabla “lookup” que mapea identificadores a enteros. El tipo asociado con E, T, F, etc., es int, y la acción semántica es fácil de implementar.
62. Interprete class Token2 { int kind; Object val; Token2(int k, Object v) { kind=k; val=v; } } final int EOF=0, ID=1, NUM=2, PLUS=3, MINUS=4,LPAREN=5, RPAREN=6, TIMES=7; int lookup(String id) { …. } int F_follow[] = {PLUS,TIMES,RPAREN,EOF}; int F() {switch(tok.kind) { case ID: int i=lookup((String)(tok.val));advance()return i ; case NUM: int i=((integer)(tok.val)).intVal(); advance(); return i ; case LPAREN: eat(LPAREN); int i=E(); eatOrSkipTo(RPAREN,F_follow); return i ; case EOF: default: print("1 esperaba ID,NUM, o parent izq"); //skipto(F_follow); return 0; }} int T_follow[]= {PLUS,RPAREN,EOF}; int T() {switch(tok.kind) { case ID: case NUM: case LPAREN: return Tprime(F()); default: print("2 esperaba ID, NUM o parent izq"); //skipto(T_follow); return 0; }} int Tprime(int a) {switch (tok.kind) { case TIMES: eat(TIMES); return Tprime(a*F()); case PLUS: case RPAREN: case EOF: return a ; default: print("3 esperaba ID, NUM o parent izq"); //skipto(T_follow); return 0; }} void eatOrSkipTo(int expected, int[] stop) { if (tok.kind==expected) eat(expected); else {print("4 esperaba ID, NUM o parent izq"); //skipto(stop);} } Acciones semánticas
63.
64.
65.
66.
67.
68. Programa de clases para Exp public abstract class ExpCh4 { public abstract int eval(); } class PlusExp extends ExpCh4 { private ExpCh4 e1,e2; public PlusExp(ExpCh4 a1, ExpCh4 a2) {e1=a1; e2=a2;} public int eval() { return e1.eval()+e2.eval(); } } class MinusExp extends ExpCh4 { private ExpCh4 e1,e2; public MinusExp(ExpCh4 a1, ExpCh4 a2) {e1=a1; e2=a2;} public int eval() { return e1.eval()-e2.eval(); } } class TimesExp extends ExpCh4 { private ExpCh4 e1,e2; public TimesExp(ExpCh4 a1, ExpCh4 a2) {e1=a1; e2=a2;} public int eval() { return e1.eval()*e2.eval(); } } class DivideExp extends ExpCh4 { private ExpCh4 e1,e2; public DivideExp(ExpCh4 a1, ExpCh4 a2) {e1=a1; e2=a2;} public int eval() { return e1.eval()/e2.eval(); } } class Identifier extends ExpCh4 { private String f0; public Identifier(String n0) {f0=n0;} public int eval() { return (7); //return lookup(f0); } } class IntegerLiteral extends ExpCh4 { private String f0; public IntegerLiteral(String n0) {f0=n0;} public int eval() { return (4); //return Integer.parseInt(f0); } }
74. Package syntaxtree; Program (MainClass m, ClassDeclList cl) MainClass (Identifier i1, Identifier i2, Statement s) Abstract class ClassDecl ClassDeclSimple (Identifier i, VarDeclList vl, MethodDeclList ml) ClassDeclExtends (Identifier i, identifier j, VarDeclList vl, MethodDeclList ml) VarDecl (Type t, Identifier i) MethodDecl (Type t, Identifier i, FormalList fl, VarDeclList vl, StatementList, Exp e) Formal (Type t, Identifier i) Abstract class Type IntArrayType () BooleanType () IntegerType () IdentifierType (String s) Abstract class Statement Block (StatementList sl) If (Exp e, Statement s1, Statement s2) While (Exp e, Statement s) Print (Exp e) Assign (Identifier i, Exp e) ArrayAssign (Identifier i, Exp e1, Exp e2)
75. (cont. figura) Abstract class Exp And (Exp e1, Exp e2) LessTha n(Exp e1, Exp e2) Plus (Exp e1, Exp e2) Minu s(Exp e1, Exp e2) Times (Exp e1, Exp e2) ArrayLoockup (Exp e1, Exp e2) ArrayLength (Exp e) Call (Exp e, Identifier i, ExpList el) IntegerLiteral (int i) True () False () IdentifierExp (String s) This () NewArray (Exp e) NewObject (Identifier i) Not (Exp e) Identifier (String s) List classes ClassDeclList () ExpList () FormalList () MethodDeclList () StatementList () VarDeclList ()
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
90.
91.
92.
93.
94.
95.
96. Método Visitador Class ErrorMsg { boolean anyErrors; void complain (String msg) { anyErrors = true; System.out.println(msg); } } // Type t; // Identifier i; Public void visit(VarDecl n) { Type t = n.t.accept(this); String id= n.i.toString(); if (currMethod ==null) { if (!currClass.addVar(id,t)) error.complain(id + “is already defined in “ + currClass.getId()); } else if (!currentMethod.addVar(id,t)) error.Complain(id + “is already defined in “ + currClass.getId( ) + “.” + currMethod.getId( ));
97.
98. Método Visitador para expresiones Plus // Exp e1, e2; Public Type visit(Plus n) { if (! (n.e1.accept(this) instanceOf IntegerType) ) error.complain(“Left side of LessThan must be of type integer”); if (! (n.e2.accept(this) instanceOf IntegerType) ) error.complain(“Right side of LessThan must be of type integer”); return new IntegerType( ); }
99.
100.
101.
102.
103.
104.
105. Ejemplo: Un marco de pila Argumentos de entrada Apuntador Del marco Argumentos De salida Apuntador De pila Argumento n … … Argumento 1 Liga estática Argumento m … … Argumento 1 Liga estática Variables locales Dirección retorno Temporales Registros salvados Marco actual Marco anterior Marco siguiente Direcciones de Memoria mas altas
106.
107.
108.
109.
110.
111.
112.
113. Programa de funciones Anidadas Type tree= {key: string, left: tree, right: tree} Function prettyprint(tree:tree): string= let var output := “ “ function write(s:string) = output :=concat(output,s) function show(n:int, t:tree) = let function indent(s:string)= (for i:= 1 to n do write(“ “); output:=concat(output,s);write(“ “); in if t=nil then indent(“.”) else (indent(t.key); show(n+1,t.left); show(n+1,t.right)) end in show(0,tree); output end
135. Por ejemplo, la función: Int foo() { int a; int b; /* body of foo */ } FP SP Tendría el registro de activación Existen dos apuntadores. FP que apunta al inicio del RA actual y SP que apunta a la primera localidad vacía. El valor retornado de una función es puesto en un registro especial en lugar de la pila. Cuando una función es llamada los valores de los parámetros de entrada son puestos en la pila, y cuando la función retorna un valor, el valor es almacenado en el registro de resultado. Registros salvados b a
136.
137. Formato completo de RA FP SP Previous Activation Record Current Activation Record Saved Registers Local Variables Input Parameter 1 Input Parameter 2 … Input Parameter n
138. Considere el siguiente programa: void foo(int a, int b); void foo(int c, int d); void main() { int u; int v; /* Label A */ bar(1,2); } void bar(int a, int b) { int w; int x; foo(3,4); } void foo(int c, int d) { int y; int z; /* Label B */ }
139. En label A de el programa, la pila de RA se miraría de la forma siguiente FP SP Activation Record for main La variable local u puede accesarse examinando la localidad de memoria apuntada por FP. La variable v puede accesarse examinando (FP-wordsize). Algo para aclarar es que la pila crece de direcciones altas a bajas. Saved Registers v u
140. En etiqueta B los RA se mirarían así: FP SP Activation Record for main Activation Record for bar Activation Record for foo Saved Registers z y c d Saved Registers x w a b Saved Registers v u
141.
142.
143.
144.
145.
146. Ejemplos: (asumiremos que wordsize=4 y que un entero se guarda en una palabra) void foo(int a, int b) { int x; int y; boolean z; x = 1; y = a * b; y++; bar(y, x + 1, a); x = function(y+1, 3); if (x > 2) z = true; else z = false; }
155. La variable local x es almacenada en la pila, igual que la dirección base del arreglo A y del arreglo B. Stack Heap x A B FP SP A[0] A[1] A[2] A[3] A[4] B[0] B[1] B[2] B[3] B[4] Saved Registers
159. Arreglos Multidimensionales se manejan de manera similar. Ejemplo: void twoDarray { int i; int c [ ] [ ] ; C = new int [3] [ ] ; for (i=0; i<3; i++) C[i] = new int[2] ; /* Body of function */ }
160. El RA y el Heap se ven así: Stack Heap C[0] C[1] C[2] C[0] [0] C[0] [1] C[1] [0] C[1] [1] C[2] [0] C[2] [1] i C FP SP Saved Registers
161. La variable C sería representada así: Memory Operator(-) Register(FP) Constant(WORDSIZE)
164. Variables Instanciadas Son muy similares a las variables arreglo. La única diferencia es que en las variables arreglo, el desplazamiento para el índice necesita ser calculado, mientras que en las variables instanciadas, el desplazamiento es conocido en tiempo de compilación. Ejemplo: class simpleClass { int x; int y; int A[ ]; } void main() { simpleClass(); s = new simpleClass(); s.A = new int[3] /* Body of main */ }
165. ¿Cuál es el árbol de ensamblador para s.y ? Memory Register(FP) Él árbol de s : Para obtener y de s : Memory Memory Operator(-) Constant(WORDSIZE) Register(FP)
166. El árbol de la variable s.x: Memory Memory Register(FP) El árbol para .A[3]: Memory Operator(-) Memory Operator(*) Operator(-) Memory Constant(2*WORDSIZE) Constant(WORDSIZE) Constant(3) Register(FP)
167.
168. Ejemplo: Void main() { int x; int y; y = x + 4; } Move Memory Operator(+) Operator(-) Constant(4) Memory Register(FP) Register(FP) Constant(4)
169.
170. Y se representa con el árbol: Sequential ConditionalJump(“iftrue”) <test> Sequential Sequential Sequential Sequential Label(“ifend”) Jump(“ifend”) Label(“iftrue”) <statement1> <statement2>
171.
172.
173.
174.
175.
176. Etiquetas Se necesitan tanto para las llamadas a métodos como para los “if” y los ciclos “while” y “for”. La siguiente clase nos genera etiquetas únicas (ifEnd001,ifEnd02,etc.) y etiquetas específicas (llamadas a métodos). import java.util.Hashtable; class Label { protected String label; private static HashTable labelhash; Integer last; Int next; public Label(String s) { if (labelhash == null) { labelhash = new HashTable(199); } last = (Integer) labelhash.find(s); If (last == null) { next = 1; labelhash.insert(s, new Integer(1)); label = s + 1; } else { next = last.intValue() + 1; labelhash.delete(s); labelhash.insert(s, new Integer(next)); label = s + next; } } public static Label AbsLabel(String s) { Label abslab = new Label(); abslab.label = s; return abslab; } public String toString() { return label; }
177. Interfase para Construir Árboles de Ensamblador Abstracto import java.util.Vector; public interface AATBuildTree { public AATSatement functionDefinition(AATStatemen body, int framesize, Label start, Label end); public AATSatement ifStatement(AATExpression test, AATStatement ifbody, AATStatement elsebody); public AATEexpression allocate(AATExpression size); public AATStatement whileStatement(AATExpression test, AATStatement increment, AATStatemen body); public AATStatement emptyStatement( ) ; public AATStatement callStatement(Vector actuals, Label name); public AATStatement assignmentStatement(AATExpression lhs, AATExpressionrhs); public AATStatement sequentialStatement(AATStatement first, AATStatement second); public AATExpression baseVariable(int offset); public AATExpression arrayVariable(AATExpresion base, AATExpression index, int elementSiza); public AATExpression classVariable(AATExpression(AATExpression base, int, offset); public AATExpression constantExpression(int, value); public AATExpression operatorExpression(AATExpression left, AATExpression rigth, int operator); public AATExpression callExpression(Vector actuals, Label name); public AATStatement returnStatement(AATExpression value, Label functioned); }
178. EJEMPLOS: void foo(int a) { int b; int c; if (a > 2) b = 2; else c = a+ 1; } Sea bt una instancia de una clase que implementa la interfase AATBuildTree, y sea 4 el tamaño de la palabra de la máquina. El árbol de ensamblador abstracto para la expresión (a > 2) en la función foo podría crearse con: AATExpression e1; e1 = bt.operatorExpression(bt.baseVariable(4), bt.constantExpression/(2), AATOperator.GREATER_THAN);
179. Igual, el ensamblador abstracto para el estatuto b = 2; y c = a + 1 , puede crearse con: AATEstatement s1, s2; s1 = bt.assignmentStatemen(bt.baseVariable(0), bt.constantExpression(2)); s2 = bt.assignment(bt.BaseVariable(-4), bt.operatorExpression(bt.baseVariable(4), bt.constantExpression(1), AATOperator.PLUS)); Finalmente, dadas las definiciones anteriores, el ensamblador abstracto para el estatuto if (a > 2) b = 2 else c = a + 1; puede crearse con: AATstatement s3; s3 = bt.ifStatement(e1,s1,s2);
180.
181.
182. Ejemplo: Para analizar un AST: Integer_Literal(3) El analizador semántico actualmente retorna IntegerType . Para analizar el AST: + Integer_Literal(3) Integer_Literal(4) El analizador semántico llama al método accept para cada subárbol – constant(3) y constant(4), se asegura que ambos sean enteros, y retorne un entero. Después de modificar el analizador semántico para producir AATs, cunado se analiza el AST: Integer_Literal(3) El analizador retornará dos valores: Integer_Type y bt.constantExpression(3).
183. Cuando se analiza el AST: + Integer_Literal(3) Integer_Literal(4) Primero se llama el método accept de los subárboles, se asegura que los tipos sean enteros y entonces construye una expresión operador basado en los valores de los subárboles (usando una llamada al método operatorExpression en la interfase AATBuildTree ).
184.
185.
186. Código Ensamblador Objeto Usaremos un subconjunto del ensamblador para MIPS if rs ≥ 0, jump to the label <target> Bgez rs, <target> if rs < 0, jump to the label <target> bltz rs, <target> if rs ≠ rt, jump to the label <target> bne rs, rt, <target> if rs == rt, jump to the label <target> beq rs, rt, <target> if rs < rt, rd = 1, else rd = 0 slt rd, rs, rt Jump to the address stored in register rs. Used in conjunction with jal to return from function and procedure calls jr rs jump and link. Put the address of the next instruction in the return register, and then jump to the address <target>. Used for function and procedure calls. jal <target> Jump to the assembly label <target>. j <target> Move contents of the special register LOW into the register rd. mflo rd Dive contents of register rs by register rt, put the quotient in register LO and the remainder in register HI div rs, rt Multiply contents of register rs by register rt, put the low order bits in register LO and the high bits in register HI mult rs, rt Add the constant value <val> to register rs, put result in register rt. addi rt, rs, <val> Subtract contents of register rt from rs, put result in register rd. sub rd, rs, rt Add contents of register rs and rt, put result in register rd. add rd, rs, rt Add the constant value <offset> to the register base to get an address. Store the contents of rt into this address. M[base + <offset>] = rt sw rt, <offset> (base) Add the constant value <offset> to the register base to get an address. Load the contents of this address into the register rt. Rt = M[base + <offset>] Description lw rt, <offset> (base) Instruction
187. Register that we will use for code generation General Purpose Register $t3 $t3 General Purpose Register $t2 $t2 General Purpose Register $t1 $t1 Accumulator Register – Used for expressioncalculation $t0 $ACC Zero register – This register always has the value 0 $zero $zero Result Register – Holds the return address for the current function $ra $return Result Register – Holds the return value for functions $v0 $result Expression Stack Pointer – The next expression stack holds temporary values for expression avaluations $esp $ESP Stack Pointer – Used for the activation record stack $sp $SP Frame pointer – points to the top of the current activation record Description $fp SPIM Name $FP Mnemonic Name
188.
189. Ejemplos: Tiling Simple para Árboles de Expresiones . El código asociado con el tile colocará el valor de la expresión en el tope de la pila de expresiones. Expresiones de Constantes Considere el siguiente árbol: Constant(5) El código asociado con este tile necesita poner un 5 en el tope de la pila de expresiones. addi $t1, $zero, 5 % Load the constant value 5 into the register $t1 sw $t1, 0($ESP) % Store $t1 on the top of the expression stack addi $ESP, $ESP, -4 % Update the expression stack pointer En general, el tile para cualquier valor constante x es: addi $t1, $zero, x % Load the constant value into x the register $t1 sw $t1, 0($ESP) % Store $t1 on the top of the expression stack addi $ESP, $ESP, -4 % Update the expression stack pointer
190. Operaciones de Aritmética Binaria + Constant(3) Constant(4) En lugar de tratar de cubrir todo el árbol con un tile, usaremos tres. La constante 4 y 5 pueden cubrirse cada una con un tile de constante, y usaremos un tile “ +” para cubrir la suma. + Constant(3) Constant(4)
191. ¿Cómo debe ser el código para +? Recordemos que emitimos código en post-order (subárbol izquierdo, subárbol derecho y luego raíz). Podemos asumir que los valores de los operandos de la izquierda y la derecha de el + ya están en la pila cuando el código de el tile + es emitido. Ntonces, todo lo que necesitamos es hacer es sacar (pop off) esos valores , hacer la suma y meter (push) el resultado de regreso a la pila. El código para el tile + es: lw $t1, 8(ESP) % Load the first operand into temporary $t1 lw $t2, 4(ESP) % Load the second operand into temporary $t2 add $t1, $t1, $t2 % Do the addition. Storing result in $t1 sw $t1, 8(ESP) % Store the result on the expression stack add $ESP, $ESP, 4 % Update the expression stack pointer
192. y el código para toda la expresión es: addi $t1, $zero, 3 % Load the constant value 3 into the register $t1 sw $t1, 0($ESP) % Store $t1 on the top of the expression stack addi $ESP, $ESP, -4 % Update the expression stack pointer addi $t1, $zero, 4 % Load the constant value into 4 the register $t1 sw $t1, 0($ESP) % Store $t1 on the top of the expression stack addi $ESP, $ESP, -4 %Update the expression stack pointer lw $t1, 8($ESP) % Load the first operand into temporary $t1 lw $t2, 4($ESP) % Load the second operand into temporary $t2 add $t1, $t1, $t2 % Do the addtion, storing result in $t1 sw $t1, 8(ESP) % Update the expression stack pointer
193. Registros Considere el árbol de expresión Register(FP) Esto ocupa un tile. sw $FP, 0($ESP) addi $ESP, ESP, -4 Combinando otros tiles discutidos antes, el árbol - Register(FP) Constant(8) Puede ser los tiles: - Register(FP) Constant(8)
194. Produciendo el árbol sw $FP, 0($ESP) % Store frame pointer on the top of the expression stack addi $ESP, $ESP, -4 % Update the expression stack pointer addi $t1, $zero, 8 % Load the constant value 8 into the register $t1 sw $t1, 0($esp) % Store $t1 on the top of the expression stack addi $ESP, $ESP, -4 % Update the expression stack pointer lw $t1, 8($ESP) % Load the first operand into temporary $t1