From 6b27930ef9c3eaede8d0c283ffa8376c40145f80 Mon Sep 17 00:00:00 2001 From: Iván Ávalos Date: Fri, 25 Nov 2022 21:29:55 -0600 Subject: ¡Otra vez hay renderizador de AST! MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- compilador/astree/decl.py | 31 +++++++++- compilador/astree/expr.py | 151 ++++++++++++++++++++++++++++++++++++--------- compilador/astree/ident.py | 1 + compilador/astree/type.py | 32 +++++++++- compilador/astree/unit.py | 14 ++++- compilador/parse/base.py | 4 +- compilador/parse/expr.py | 15 +++-- compilador/parse/type.py | 2 +- compilador/parser.py | 7 ++- pruebas/sintaxis2.es | 1 + 10 files changed, 212 insertions(+), 46 deletions(-) diff --git a/compilador/astree/decl.py b/compilador/astree/decl.py index 1e6de1d..99f5f75 100644 --- a/compilador/astree/decl.py +++ b/compilador/astree/decl.py @@ -1,6 +1,9 @@ +import uuid +import graphviz as gv from dataclasses import dataclass -from typing import Optional +from typing import Optional, cast +from astree.graphable import Graphable from astree.type import Type from astree.ident import Ident from astree.expr import Expr @@ -9,19 +12,41 @@ from astree.expr import Expr # # entero a = 0; @dataclass -class DeclGlobal: +class DeclGlobal(Graphable): ident: Ident _type: Type init: Expr + def graph(self, dot: gv.Digraph, parent: str = None, edge: str = None) -> None: + name = uuid.uuid1().hex + dot.node(name, 'DeclGlobal') + dot.edge(parent, name, label = edge) + name_ident = uuid.uuid1().hex + dot.node(name_ident, self.ident) + dot.edge(name, name_ident, label = 'ident') + if isinstance(self.init, Graphable): + self.init.graph(dot, name, 'init') + # A function declaration. # # funcion vacio main() { ... } @dataclass -class DeclFunc: +class DeclFunc(Graphable): ident: Ident prototype: Type body: Optional[Expr] + def graph(self, dot: gv.Digraph, parent: str = None, edge: str = None) -> None: + name = uuid.uuid1().hex + dot.node(name, 'DeclFunc') + dot.edge(parent, name, label = edge) + name_ident = uuid.uuid1().hex + dot.node(name_ident, self.ident) + dot.edge(name, name_ident, label = 'ident') + if isinstance(self.prototype, Graphable): + self.prototype.graph(dot, name, 'prototype') + if self.body and isinstance(self.body, Graphable): + self.body.graph(dot, name, 'body') + # A Javañol declaration Decl = DeclGlobal | DeclFunc diff --git a/compilador/astree/expr.py b/compilador/astree/expr.py index 527e606..b696d51 100644 --- a/compilador/astree/expr.py +++ b/compilador/astree/expr.py @@ -1,7 +1,10 @@ +import uuid +import graphviz as gv from dataclasses import dataclass from enum import Enum, auto from typing import List, Optional +from astree.graphable import Graphable from astree.type import Type from astree.ident import Ident @@ -10,7 +13,14 @@ Expr = None # An identifier access expression. # # a -AccessIdentifier = Ident +@dataclass +class AccessIdentifier(Graphable): + ident: Ident + + def graph(self, dot: gv.Digraph, parent: str = None, edge: str = None) -> None: + name = uuid.uuid1().hex + dot.node(name, self.ident) + dot.edge(parent, name, label = edge) # An access expression. AccessExpr = AccessIdentifier @@ -19,10 +29,18 @@ AccessExpr = AccessIdentifier # # a = 10 @dataclass -class AssignExpr: - _object: Expr +class AssignExpr(Graphable): + _object: AccessExpr value: Expr + def graph(self, dot: gv.Digraph, parent: str = None, edge: str = None) -> None: + name = uuid.uuid1().hex + dot.node(name, 'AssignExpr') + dot.edge(parent, name, label = edge) + self._object.graph(dot, name, 'object') + if isinstance(self.value, Graphable): + self.value.graph(dot, name, 'value') + # A binary arithmetic operator. class BinarithmOp(Enum): BAND = '&' @@ -44,26 +62,51 @@ class BinarithmOp(Enum): # # a * b @dataclass -class BinarithmExpr: +class BinarithmExpr(Graphable): op: BinarithmOp lvalue: Expr rvalue: Expr + def graph(self, dot: gv.Digraph, parent: str = None, edge: str = None) -> None: + name = uuid.uuid1().hex + dot.node(name, 'BinarithmExpr') + dot.edge(parent, name, label = edge) + name_op = uuid.uuid1().hex + dot.node(name_op, self.op.value) + dot.edge(name, name_op, 'op') + if isinstance(self.lvalue, Graphable): + self.lvalue.graph(dot, name, 'lvalue') + if isinstance(self.rvalue, Graphable): + self.rvalue.graph(dot, name, 'rvalue') + # A break expression. # # detener @dataclass -class BreakExpr: - pass +class BreakExpr(Graphable): + def graph(self, dot: gv.Digraph, parent: str = None, edge: str = None) -> None: + name = uuid.uuid1().hex + dot.node(name, 'BreakExpr') + dot.edge(parent, name, label = edge) # A function call expression. # # foo(bar) @dataclass -class CallExpr: +class CallExpr(Graphable): lvalue: Expr args: List[Expr] + def graph(self, dot: gv.Digraph, parent: str = None, edge: str = None) -> None: + name = uuid.uuid1().hex + dot.node(name, 'CallExpr') + dot.edge(parent, name, label = edge) + if isinstance(self.lvalue, Graphable): + self.lvalue.graph(dot, name, 'lvalue') + for a in self.args: + if isinstance(a, Graphable): + a.graph(dot, name, 'arg') + # A compound expression. # # { @@ -72,53 +115,84 @@ class CallExpr: # // ... # } @dataclass -class CompoundExpr: +class CompoundExpr(Graphable): exprs: List[Expr] + def graph(self, dot: gv.Digraph, parent: str = None, edge: str = None) -> None: + name = uuid.uuid1().hex + dot.node(name, 'CompoundExpr') + dot.edge(parent, name, label = edge) + for e in self.exprs: + if isinstance(e, Graphable): + e.graph(dot, name, 'expr') + # A continue expression. # # continuar @dataclass -class ContinueExpr: - pass +class ContinueExpr(Graphable): + def graph(self, dot: gv.Digraph, parent: str = None, edge: str = None) -> None: + name = uuid.uuid1().hex + dot.node(name, 'ContinueExpr') + dot.edge(parent, name, label = edge) # A scalar value. -Value = bool | str | int | type(None) +@dataclass +class Value(Graphable): + value: bool | str | int + + def graph(self, dot: gv.Digraph, parent: str = None, edge: str = None) -> None: + name = uuid.uuid1().hex + dot.node(name, str(self.value)) + dot.edge(parent, name, label = edge) # An integer constant. @dataclass -class NumberConstant: +class NumberConstant(Graphable): value: int + def graph(self, dot: gv.Digraph, parent: str = None, edge: str = None) -> None: + name = uuid.uuid1().hex + dot.node(name, str(self.value)) + dot.edge(parent, name, label = edge) + # A constant expression. ConstantExpr = Value | NumberConstant -# A for loop. -# -# porcada (entero a = 0; a < b; a++) {} -@dataclass -class ForExpr: - bindings: Optional[Expr] - cond: Expr - afterthought: Optional[Expr] - body: Expr - # An if or if..else expression. # # si (a) { } sino { } @dataclass -class IfExpr: +class IfExpr(Graphable): cond: Expr tbranch: Expr fbranch: Optional[Expr] + def graph(self, dot: gv.Digraph, parent: str = None, edge: str = None) -> None: + name = uuid.uuid1().hex + dot.node(name, 'IfExpr') + dot.edge(parent, name, label = edge) + if isinstance(self.cond, Graphable): + self.cond.graph(dot, name, 'cond') + if isinstance(self.tbranch, Graphable): + self.tbranch.graph(dot, name, 'tbranch') + if self.fbranch and isinstance(self.fbranch, Graphable): + self.fbranch.graph(dot, name, 'fbranch') + # A print statement. # # imprimir a @dataclass -class PrintExpr: +class PrintExpr(Graphable): expr: Expr + def graph(self, dot: gv.Digraph, parent: str = None, edge: str = None) -> None: + name = uuid.uuid1().hex + dot.node(name, 'PrintExpr') + dot.edge(parent, name, label = edge) + if isinstance(self.expr, Graphable): + self.expr.graph(dot, name, 'expr') + # A read statement. # # leer a @@ -126,6 +200,12 @@ class PrintExpr: class ReadExpr: expr: AccessExpr + def graph(self, dot: gv.Digraph, parent: str = None, edge: str = None) -> None: + name = uuid.uuid1().hex + dot.node(name, 'ReadExpr') + dot.edge(parent, name, label = edge) + self.expr.graph(dot, name, 'expr') + # A return statement. # # retorna a @@ -133,16 +213,31 @@ class ReadExpr: class ReturnExpr: expr: Optional[Expr] + def graph(self, dot: gv.Digraph, parent: str = None, edge: str = None) -> None: + name = uuid.uuid1().hex + dot.node(name, 'ReturnExpr') + dot.edge(parent, name, label = edge) + if self.expr and isinstance(self.expr, Graphable): + self.expr.graph(dot, name, 'expr') + # A while expression. # # mientras (cond) { } @dataclass -class WhileExpr: +class WhileExpr(Graphable): cond: Expr body: Expr + def graph(self, dot: gv.Digraph, parent: str = None, edge: str = None) -> None: + name = uuid.uuid1().hex + dot.node(name, 'WhileExpr') + dot.edge(parent, name, label = edge) + if isinstance(self.cond, Graphable): + self.cond.graph(dot, name, 'cond') + if isinstance(self.body, Graphable): + self.body.graph(dot, name, 'body') + # A Javañol expression. Expr = (AccessExpr | AssignExpr | BinarithmExpr | BreakExpr | - CallExpr | ConstantExpr | ContinueExpr | ForExpr | - IfExpr | CompoundExpr | PrintExpr | ReadExpr | - ReturnExpr) + CallExpr | ConstantExpr | ContinueExpr | IfExpr | + CompoundExpr | PrintExpr | ReadExpr | ReturnExpr) diff --git a/compilador/astree/ident.py b/compilador/astree/ident.py index 936b745..ecc7edf 100644 --- a/compilador/astree/ident.py +++ b/compilador/astree/ident.py @@ -1,3 +1,4 @@ from typing import List +from dataclasses import dataclass Ident = str diff --git a/compilador/astree/type.py b/compilador/astree/type.py index 5389702..b254b91 100644 --- a/compilador/astree/type.py +++ b/compilador/astree/type.py @@ -1,7 +1,11 @@ +import uuid +import graphviz as gv +from pprint import pformat from dataclasses import dataclass from typing import List from enum import Enum +from astree.graphable import Graphable from tabla import Token Type = None @@ -14,16 +18,40 @@ class BuiltinType(Enum): INT = Token.INT VOID = Token.VOID + def __str__(self): + return self.value.value + # A parameter to a function type. @dataclass -class FuncParam: +class FuncParam(Graphable): name: str _type: Type + def graph(self, dot: gv.Digraph, parent: str = None, edge: str = None) -> None: + name = uuid.uuid1().hex + dot.node(name, 'FuncParam') + dot.edge(parent, name, label = edge) + name_name = uuid.uuid1().hex + dot.node(name_name, self.name) + dot.edge(name, name_name, 'name') + name_type = uuid.uuid1().hex + dot.node(name_type, str(self._type)) + dot.edge(name, name_type, 'type') + # funcion vacio ... (a: int, b: int ...) @dataclass -class FuncType: +class FuncType(Graphable): result: Type params: List[FuncParam] + def graph(self, dot: gv.Digraph, parent: str = None, edge: str = None) -> None: + name = uuid.uuid1().hex + dot.node(name, 'FuncType') + dot.edge(parent, name, label = edge) + name_result = uuid.uuid1().hex + dot.node(name_result, str(self.result)) + dot.edge(name, name_result, 'result') + for p in self.params: + p.graph(dot, name, 'param') + Type = BuiltinType diff --git a/compilador/astree/unit.py b/compilador/astree/unit.py index 8ffdf19..c089214 100644 --- a/compilador/astree/unit.py +++ b/compilador/astree/unit.py @@ -1,9 +1,21 @@ +import uuid +import graphviz as gv from dataclasses import dataclass from typing import List +from astree.graphable import Graphable from astree.decl import Decl # A single compilation unit, representing all of the members of a namespace. @dataclass -class Unit: +class Unit(Graphable): decls: List[Decl] + + def graph(self, dot: gv.Digraph, parent: str = None, edge: str = None) -> None: + name = uuid.uuid1().hex + dot.node(name, 'Unit') + if parent: + dot.edge(name, parent, label = edge) + for d in self.decls: + if isinstance(d, Graphable): + d.graph(dot, name, 'decl') diff --git a/compilador/parse/base.py b/compilador/parse/base.py index 9ee673b..bf9277d 100644 --- a/compilador/parse/base.py +++ b/compilador/parse/base.py @@ -61,10 +61,10 @@ class BaseParser: def unlex(self): self.iterador.back() - def synassert(self, cond: bool, msg: str) -> (Error | NoReturn): + def synassert(self, cond: bool, msg: str, numlinea: int) -> (Error | NoReturn): '''Returns a syntax error if cond is false and void otherwise. ''' if not cond: - return Error(msg = msg) + return Error(msg = msg, numlinea = numlinea) diff --git a/compilador/parse/expr.py b/compilador/parse/expr.py index 6608f6a..2401f30 100644 --- a/compilador/parse/expr.py +++ b/compilador/parse/expr.py @@ -33,7 +33,8 @@ class ParseExpr: error = self.parser.synassert( isinstance(obj, AccessExpr), - "Se esperaba un objeto como destino de la asignación.") + "Se esperaba un objeto como destino de la asignación.", + numlinea = tok.numlinea) if type(error) is Error: return error @@ -88,7 +89,7 @@ class ParseExpr: # Expresión expr = self.expr() if type(expr) is Error: - return epr + return expr args.append(expr) @@ -136,12 +137,10 @@ class ParseExpr: def constant(self) -> (Expr | Error): tok: LexToken = self.parser.lex() expr: Optional[ConstantExpr] = None - if tok.tipo == Token.STRING_LIT: - expr: str = tok.valor + if tok.tipo in [Token.STRING_LIT, Token.BOOLEAN_LIT]: + expr = Value(value = tok.valor) elif tok.tipo == Token.INT_LIT: expr = NumberConstant(value = tok.valor) - elif tok.tipo == Token.BOOLEAN_LIT: - expr: bool = tok.valor else: return Error(msg = "Se esperaba una constante.", numlinea = tok.numlinea) return expr @@ -260,7 +259,7 @@ class ParseExpr: if type(ident) is Error: return ident - return ReadExpr(expr = ident) + return ReadExpr(expr = AccessExpr(ident = ident)) def plain_expression(self) -> (Expr | Error): tok: LexToken = self.parser.peek() @@ -281,7 +280,7 @@ class ParseExpr: ident = ParseIdent(self.parser).ident() if type(ident) is Error: return ident - return ident + return AccessIdentifier(ident = ident) def unarithm(self) -> (Expr | Error): if self.parser._try(Token.L_BRACKET): diff --git a/compilador/parse/type.py b/compilador/parse/type.py index 06c68d0..1fd0957 100644 --- a/compilador/parse/type.py +++ b/compilador/parse/type.py @@ -36,7 +36,7 @@ class ParseType: if type(name) is Error: return name - params.append(FuncParam(name = name, + params.append(FuncParam(name = name.nombre, _type = __type)) # , diff --git a/compilador/parser.py b/compilador/parser.py index 99cabe7..e7f86db 100644 --- a/compilador/parser.py +++ b/compilador/parser.py @@ -1,4 +1,5 @@ import sys +import graphviz as gv from pprint import pprint from tabla import TablaLex @@ -8,6 +9,7 @@ from parse.unit import ParseUnit class Parser: def __init__(self, input_file: str): + self.input_file = input_file self.tabla = TablaLex() self.tabla.importar(input_file + '.tab') self.iterador = self.tabla.iterar() @@ -19,5 +21,8 @@ class Parser: print (unit.message, file=sys.stderr) return 1 - pprint(unit) + dot = gv.Digraph() + dot.attr('node', fontname='monospace') + unit.graph(dot) + dot.render(self.input_file + '.gv') return 0 diff --git a/pruebas/sintaxis2.es b/pruebas/sintaxis2.es index 09e5f1f..93cfdb7 100644 --- a/pruebas/sintaxis2.es +++ b/pruebas/sintaxis2.es @@ -5,6 +5,7 @@ caracter d; funcion entero a (entero a, cadena b) { b = ((40 - 2) + 10) * 2 + b(10); imprimir ("Hola"); + imprimir (1 + 2); si (a >= 10) { imprimir (b + 20); } sino b; -- cgit v1.2.3