From c7f014aef2c86d7a17f2642180732a6ba9b2a125 Mon Sep 17 00:00:00 2001 From: Iván Ávalos Date: Fri, 25 Nov 2022 16:33:08 -0600 Subject: ¡Ya hay funciones y (algunas) expresiones! MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- compilador/astree/expr.py | 29 ++++-- compilador/errors.py | 7 +- compilador/parse/decl.py | 6 +- compilador/parse/expr.py | 255 +++++++++++++++++++++++++++++++++++++++++++++- pruebas/sintaxis2.es | 11 +- 5 files changed, 290 insertions(+), 18 deletions(-) diff --git a/compilador/astree/expr.py b/compilador/astree/expr.py index 1bb7f4f..d5a2cb0 100644 --- a/compilador/astree/expr.py +++ b/compilador/astree/expr.py @@ -3,10 +3,19 @@ from enum import Enum, auto from typing import List, Optional from astree.type import Type +from astree.ident import Ident Expr = None -# An assignment expression +# An identifier access expression. +# +# a +AccessIdentifier = Ident + +# An access expression. +AccessExpr = AccessIdentifier + +# An assignment expression. # # a = 10 @dataclass @@ -14,7 +23,7 @@ class AssignExpr: _object: Expr value: Expr -# A binary arithmetic operator +# A binary arithmetic operator. class BinarithmOp(Enum): BAND = '&' BOR = '|' @@ -92,19 +101,23 @@ class IfExpr: # A print statement. # # imprimir a -PrintExpr = Expr +@dataclass +class PrintExpr: + expr: Expr # A read statement. # # leer a -ReadExpr = Expr +@dataclass +class ReadExpr: + expr: AccessExpr # A return statement. # # return a ReturnExpr = Optional[Expr] -# A Javañol expression -Expr = (AssignExpr | BinarithmExpr | CallExpr | ConstantExpr | - ForExpr | IfExpr | CompoundExpr | PrintExpr | - ReadExpr | ReturnExpr) +# A Javañol expression. +Expr = (AccessExpr | AssignExpr | BinarithmExpr | CallExpr | + ConstantExpr | ForExpr | IfExpr | CompoundExpr | + PrintExpr | ReadExpr | ReturnExpr) diff --git a/compilador/errors.py b/compilador/errors.py index 5a4e7a2..6bb05b8 100644 --- a/compilador/errors.py +++ b/compilador/errors.py @@ -15,8 +15,11 @@ class Error: 'S_ESPERA_PC': 'Se esperaba `;`', } - def __init__(self, msg: str = None): - self.message = msg + def __init__(self, msg: str = None, numlinea: int = None): + if numlinea: + self.message = "Error en la línea %d: %s" % (numlinea, msg) + else: + self.message = msg @classmethod def lex(self, error, numlinea: int): diff --git a/compilador/parse/decl.py b/compilador/parse/decl.py index 93666fc..aa9e23a 100644 --- a/compilador/parse/decl.py +++ b/compilador/parse/decl.py @@ -58,9 +58,13 @@ class ParseDecl: if type(proto) is Error: return proto + init = ParseExpr(self.parser).compound_expr() + if type(init) is Error: + return init + return DeclFunc(ident = ident, prototype = proto, - body = None) + body = init) # Parses a declaration. def decl(self) -> (Decl | Error): diff --git a/compilador/parse/expr.py b/compilador/parse/expr.py index 23b1a61..492b4e6 100644 --- a/compilador/parse/expr.py +++ b/compilador/parse/expr.py @@ -1,12 +1,257 @@ +from typing import NoReturn, Optional, cast + +from tabla import Token, LexToken from parse.base import BaseParser +from parse.ident import ParseIdent from errors import Error -from astree.expr import Expr +from astree.expr import Expr, BinarithmOp, ConstantExpr, NumberConstant, CallExpr, PrintExpr, BinarithmExpr, CompoundExpr, ReadExpr, AccessExpr, AssignExpr class ParseExpr: def __init__(self, parser: BaseParser): self.parser = parser - def expr(self) -> Expr | Error: - self.parser.iterador.next() - return - + def expr(self) -> (Expr | Error): + obj = self.binarithm(None, 0) + + # = + tok = self.parser._try(Token.EQUAL) + if not tok: + return obj + + error = self.parser.synassert( + isinstance(obj, AccessExpr), + "Se esperaba un objeto como destino de la asignación.") + if type(error) is Error: + return error + + # Expresión + expr = self.expr() + if type(expr) is Error: + return expr + + return AssignExpr(_object = obj, + value = expr) + + # WIP + def binarithm(self, lvalue: Expr, i: int) -> (Expr | Error): + print(f'binarithm({lvalue}, {i})') + _lvalue = lvalue + if not lvalue: + _lvalue = self.cast(lvalue) + if type(_lvalue) is Error: + return _lvalue + print(f'lvalue = {_lvalue}') + + tok = self.parser.lex() + print(f'tok = {tok}') + j: int = self.precedence(tok.tipo) + while j >= i: + op = self.binop_for_tok(tok.tipo) + + rvalue = self.cast(_lvalue) + if type(rvalue) is Error: + return rvalue + + tok = self.parser.lex() + k: int = self.precedence(tok.tipo) + while k > j: + self.parser.unlex() + rvalue = self.binarithm(rvalue, k) + if type(rvalue) is Error: + return rvalue + tok = self.parser.lex() + + k = self.precedence(tok.tipo) + + _lvalue = BinarithmExpr(op = op, + lvalue = _lvalue, + rvalue = rvalue) + + j = self.precedence(tok.tipo) + + self.parser.unlex() + return _lvalue + + def call(self, lvalue: Expr) -> (Expr | Error): + args: List[Expr] = [] + + while not self.parser._try(Token.R_PAREN): + # Expresión + expr = self.expr() + if type(expr) is Error: + return epr + + args.append(expr) + + # , + if self.parser._try(Token.COMMA): + continue + + # ) + if self.parser._try(Token.R_PAREN): + break + + return CallExpr(lvalue = lvalue, + args = args) + + def compound_expr(self) -> (Expr | Error): + items: List[Expr] = [] + + # { + lbracket = self.parser.want(Token.L_BRACKET) + if type(lbracket) is Error: + return lbracket + + while True: + # } + item = self.parser.peek(Token.R_BRACKET) + if item: break + + item = self.expr() + if type(item) is Error: + return item + + items.append(item) + + # ; + semicolon = self.parser.want(Token.SEMICOLON) + if type(semicolon) is Error: + return semicolon + + # } + rbracket = self.parser.want(Token.R_BRACKET) + if type(rbracket) is Error: + return rbracket + + return CompoundExpr(exprs = items) + + def builtin(self) -> (Expr | Error): + tok: LexToken = self.parser.peek(Token.PRINT, Token.READ) + if not tok: + return self.postfix(None) + + if tok.tipo == Token.PRINT: + return self.print_expr() + elif tok.tipo == Token.READ: + return self.read_expr() + + + def postfix(self, lvalue: Optional[Expr]) -> (Expr | Error): + _lvalue: Optional[Expr] = lvalue + if not lvalue: + _lvalue = self.plain_expression() + if type(_lvalue) == Error: + return _lvalue + + tok: LexToken = self.parser._try(Token.L_PAREN) + if not tok: + return _lvalue + + _next: Optional[LexToken] = None + if tok == Token.L_PAREN: + return self.call(_lvalue) + + return self.postfix(_next) + + def print_expr(self) -> (Expr | Error): + _print = self.parser.want(Token.PRINT) + if type(_print) is Error: + return _print + + lparen = self.parser.want(Token.L_PAREN) + if type(lparen) is Error: + return lparen + + expr = self.expr() + if type(expr) is Error: + return expr + + rparen = self.parser.want(Token.R_PAREN) + if type(rparen) is Error: + return rparen + + return PrintExpr(expr = expr) + + def read_expr(self) -> (Expr | Error): + _read = self.parser.want(Token.READ) + if type(_read) is Error: + return _read + + ident = ParseIdent(self.parser).ident() + if type(ident) is Error: + return ident + + return ReadExpr(expr = ident) + + # WIP + def cast(self, lvalue: Optional[Expr]) -> (Expr | Error): + return self.unarithm() + + # WIP + def constant(self) -> (Expr | Error): + tok: LexToken = self.parser.lex() + expr: Optional[ConstantExpr] = None + if tok.tipo == Token.STRING_LIT: + expr: str = 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 + + # WIP + def plain_expression(self) -> (Expr | Error): + tok: LexToken = self.parser.peek() + if tok.tipo in [Token.BOOLEAN_LIT, Token.CHAR_LIT, Token.INT_LIT, Token.STRING_LIT]: + return self.constant() + elif tok.tipo == Token.L_PAREN: + lparen = self.parser.want(Token.L_PAREN) + if type(lparen) is Error: + return lparen + expr = self.expr() + if type(expr) is Error: + return expr + rparen = self.parser.want(Token.R_PAREN) + if type(rparen) is Error: + return rparen + elif tok.tipo == Token.IDENT: + ident = ParseIdent(self.parser).ident() + if type(ident) is Error: + return ident + return ident + + def unarithm(self) -> (Expr | Error): + return self.builtin() + + def binop_for_tok(self, tok: Token) -> (BinarithmOp | NoReturn): + if tok is Token.SLASH: + return BinarithmOp.DIV + elif tok is Token.GT: + return BinarithmOp.GT + elif tok is Token.GEQ: + return BinarithmOp.GTEQ + elif tok is Token.EQEQ: + return BinarithmOp.LEQUAL + elif tok is Token.LT: + return BinarithmOp.LESS + elif tok is Token.LEQ: + return BinarithmOp.LESSEQ + elif tok is Token.MINUS: + return BinarithmOp.MINUS + elif tok is Token.NOTEQ: + return BinarithmOp.NEQUAL + elif tok is Token.PLUS: + return BinarithmOp.PLUS + elif tok is Token.TIMES: + return BinarithmOp.TIMES + + def precedence(self, tok: Token) -> int: + if tok in [Token.EQEQ, Token.NOTEQ]: + return 0 + elif tok in [Token.PLUS, Token.MINUS]: + return 1 + elif tok in [Token.TIMES, Token.SLASH]: + return 2 + return -1 diff --git a/pruebas/sintaxis2.es b/pruebas/sintaxis2.es index c89bee0..fdb9dd3 100644 --- a/pruebas/sintaxis2.es +++ b/pruebas/sintaxis2.es @@ -2,5 +2,12 @@ entero a = 10; entero b = 20; booleano c = verdadero; caracter d; -funcion entero a (entero a, cadena b); -funcion caracter b (booleano a); \ No newline at end of file +funcion entero a (entero a, cadena b) { + b = 40 + 2 * 10; + imprimir ("Hola"); + leer d; +}; +funcion caracter b (booleano a) { + imprimir ("Adiós"); + leer x; +}; \ No newline at end of file -- cgit v1.2.3