Jugando con pyparsing
Para un proyecto personal, estoy trabajando en un prototipo de pseudo-lenguaje like SQL, como primera instancia, estoy trabajando con pyparsing, para luego continuar en una segunda fase con algo más maduro con Lex y YACC.
Pyparsing, es una librería que permite realizar un parser de forma muy sencilla, cuenta con muchos ejemplos y una documentación decente..
El siguiente trozo de código es sencillo, vamos a tratar de parsear con pyparsing un string que contiene una serie de número naturales y operandos descritos en forma RPN (Notación Polaca Inversa).
La RPN (Notiación Polaca Inversa), no utiliza parentesis, si no, mantiene un orden de ingreso de datos, operando operando operador, ejemplo 4 4 +, que es igual a 8.
Definimos nuestro statement principal rpn_stmt con una grámatica descendente, luego nuestro operandos (enteros), operadores (+ – * /) y una expresión ( operando operando + operador ), finalmente nuestra statement principal (rpn_stmt) es un conjunto de una expresión o más expresiones.
Ahora, falta analizar los tokens que nos va a devolver nuestro parser, para lo cuál he creado una pequeña clase llamada RPNSimpleParser, ella toma cada token y realiza la siguiente tarea.
Mientras el token no sea un operador, agregamos el operando al stack (push, append), si el token es un operador, sacamos dos operandos (pop), realizamos la operación y guardamos el resultado en el stack.
#!/usr/bin/env python # rpn_simple_parser.py ( is just one simple example) # @author deerme.org from pyparsing import * ParserElement.enablePackrat() # Language Definition rpn_stmt = Forward() expr = Forward().setName("expression") integer = Regex(r"[+-]?\d+") operating = ( integer ) operator = oneOf('+ - * /') expr << ( operating + operating + operator | operating + operator ) # Main statement rpn_stmt << ( expr + ZeroOrMore( expr ) ) class RPNSimpleParser: def __init__(self,parser): self._parser = parser def execute(self,query): print "Query\t" + query tokens = self._parser.parseString( query ) print "Tokens\t" + str( tokens ) # We analyze the arrays of tokens stack = [] for i in range( 0 , len(tokens) ): if ( tokens[i] == "+" or tokens[i] == "-" or tokens[i] == "/" or tokens[i] == "*" ): op2 = int(stack.pop()) op1 = int(stack.pop()) if ( tokens[i] == "+" ): r = op1 + op2 if ( tokens[i] == "-" ): r = op1 - op2 if ( tokens[i] == "/" ): r = op1 / op2 if ( tokens[i] == "*" ): r = op1 * op2 stack.append(r) else: stack.append( tokens[i] ) if len(stack) > 0 : print stack.pop() rpn = RPNSimpleParser( rpn_stmt ) rpn.execute("10 20 + 3 /") rpn.execute("15 3 * 5 + 10 / 300 -")