Source code for PySpice.Spice.Expression.Parser

####################################################################################################
#
# PySpice - A Spice Package for Python
# Copyright (C) 2017 Fabrice Salvaire
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
#
####################################################################################################

"""This module implements a parser for Spice expressions.
"""

####################################################################################################

import logging

####################################################################################################

import ply.lex as lex
import ply.yacc as yacc

####################################################################################################

from .Ast import *

####################################################################################################

_module_logger = logging.getLogger(__name__)

####################################################################################################

# def ensure_statement_list(x):
#     if isinstance(x, StatementList):
#         return x
#     else:
#         return StatementList(x)

####################################################################################################

[docs]class Parser: _logger = _module_logger.getChild('Parser') ############################################## reserved = { } tokens = [ 'NAME', # 'INT', 'FLOAT', 'NUMBER', 'SEMICOLON', 'LEFT_PARENTHESIS', 'RIGHT_PARENTHESIS', 'SET', 'NOT', 'POWER', 'MULTIPLY', 'DIVIDE', 'INT_DIVIDE', 'MODULO', 'PLUS', 'MINUS', 'EQUAL', 'NOT_EQUAL', 'LESS', 'GREATER', 'LESS_EQUAL', 'GREATER_EQUAL', 'AND', 'OR', 'IF', 'COLON', ] + list(reserved.values()) ##############################################
[docs] def t_error(self, token): self._logger.error("Illegal character '%s' at line %u and position %u" % (token.value[0], token.lexer.lineno, token.lexer.lexpos - self._previous_newline_position)) # token.lexer.skip(1) raise NameError('Lexer error')
############################################## t_ignore = ' \t'
[docs] def t_newline(self, t): r'\r?\n+' # Track newline t.lexer.lineno += len(t.value) self._previous_newline_position = t.lexer.lexpos
# t.type = 'SEMICOLON' # return t t_ignore_COMMENT = r'\#[^\n]*' ############################################## t_SEMICOLON = r';' t_LEFT_PARENTHESIS = r'\(' t_RIGHT_PARENTHESIS = r'\)' t_SET = r'=' t_NOT = r'!' t_POWER = r'\*\*' t_MULTIPLY = r'\*' t_DIVIDE = r'/' t_MODULO = r'%' t_INT_DIVIDE = r'\/' t_PLUS = r'\+' t_MINUS = r'-' t_EQUAL = r'==' t_NOT_EQUAL = r'!=' t_LESS = r'<' t_GREATER = r'>' t_LESS_EQUAL = r'<=' t_GREATER_EQUAL = r'>=' t_AND = r'&&' t_OR = r'\|\|' t_IF = r'\?' t_COLON = r':'
[docs] def t_NAME(self, t): r'[a-zA-Z_][a-zA-Z_0-9]*' # Check for reserved words t.type = self.reserved.get(t.value, 'NAME') # Fixme: ??? return t
# def t_INT(self, t): # r'\d+' # t.value = int(t.value) # return t # exponent_part = r"""([eE][-+]?[0-9]+)""" # fractional_constant = r"""([0-9]*\.[0-9]+)|([0-9]+\.)""" # floating_constant = '(((('+fractional_constant+')'+exponent_part+'?)|([0-9]+'+exponent_part+'))[FfLl]?)'
[docs] def t_NUMBER(self, t): # 1 1. 1.23 .1 # r'\d*\.?\d+([eE][-+]?\d+)?' # r'(\d+)(\.\d+)(e(\+|-)?(\d+))? | (\d+)e(\+|-)?(\d+)' r'\d+\.\d+(e(\+|-)?(\d+))? | \d+\.(e(\+|-)?(\d+))? | \.\d+(e(\+|-)?(\d+))? | \d+' t.value = t.value return t
############################################## # # Grammar # # from lowest precedence = ( ('left', 'IF'), ('left', 'OR'), ('left', 'AND'), ('left', 'GREATER', 'LESS', 'GREATER_EQUAL', 'LESS_EQUAL', 'NOT_EQUAL', 'EQUAL'), ('left', 'MINUS', 'PLUS'), ('left', 'INT_DIVIDE', 'MODULO', 'DIVIDE', 'MULTIPLY'), ('left', 'POWER'), ('left', 'NOT'), # , 'NEGATION' )
[docs] def p_error(self, p): if p: self._logger.error("Syntax error at '%s'", p.value) raise NameError('Syntax Error') else: self._logger.error("Syntax Error at End Of File") raise NameError("Syntax Error at End Of File" )
# start = 'program' start = 'statement' # def p_empty(self, p): # 'empty :' # pass
[docs] def p_statement(self, t): 'statement : expression' print('statement', t[1])
# def p_program(self, p): # '''program : statement # | program statement # | empty # ''' # if len(p) == 3: # statement = p[2] # else: # statement = p[1] # if statement is not None: # self._program.add(statement) # def p_statement(self, p): # '''statement : expression_statement # ''' # p[0] = p[1] # def p_expression_statement(self, p): # '''expression_statement : assignation SEMICOLON # | function SEMICOLON # | SEMICOLON # ''' # if len(p) == 3: # p[0] = p[1] # def p_statement_list(self, p): # '''statement_list : statement # | statement_list statement # ''' # if len(p) == 3: # p[1].add(p[2]) # p[0] = p[1] # else: # p[0] = StatementList(p[1]) # def p_expression_list(self, p): # '''expression_list : expression # | expression_list COMMA expression # ''' # if len(p) == 3: # p[1].add(p[2]) # p[0] = p[1] # else: # p[0] = StatementList(p[1]) # def p_function(self, p): # '''function : NAME LEFT_PARENTHESIS expression_list RIGHT_PARENTHESIS # | NAME LEFT_PARENTHESIS RIGHT_PARENTHESIS # ''' # if len(p) == 5: # p[0] = Function(p[1], p[3]) # else: # p[0] = Function(p[1])
[docs] def p_variable(self, p): '''variable : NAME ''' p[0] = Variable(p[1])
# def p_assignation(self, p): # 'assignation : variable SET expression' # p[0] = Assignation(p[3], p[1]) # eval value first # def p_interger(self, p): # '''constant : INT # ''' # p[0] = IntConstant(p[1])
[docs] def p_float(self, p): '''constant : NUMBER ''' if '.' in p[1]: p[0] = FloatConstant(p[1]) else: p[0] = IntConstant(p[1])
[docs] def p_value(self, p): '''expression : variable | constant ''' p[0] = p[1]
[docs] def p_unnary_operation(self, p): # OP ... '''expression : MINUS expression | NOT expression ''' p[0] = OperatorMetaclass.get_unary(p[1])(p[2])
[docs] def p_binary_operation(self, p): # ... OP ... '''expression : expression POWER expression | expression MULTIPLY expression | expression DIVIDE expression | expression MODULO expression | expression INT_DIVIDE expression | expression PLUS expression | expression MINUS expression | expression EQUAL expression | expression NOT_EQUAL expression | expression LESS expression | expression GREATER expression | expression LESS_EQUAL expression | expression GREATER_EQUAL expression | expression AND expression | expression OR expression ''' p[0] = OperatorMetaclass.get_binary(p[2])(p[1], p[3])
[docs] def p_if(self, p): '''expression : expression IF expression COLON expression ''' p[0] = If(p[1], p[3], p[5])
############################################## def __init__(self): self._build() ############################################## def _build(self, **kwargs): self._lexer = lex.lex(module=self, **kwargs) self._parser = yacc.yacc(module=self, **kwargs) ############################################## def _reset(self): self._previous_newline_position = 0 # self._program = Program() ##############################################
[docs] def parse(self, text): self._reset() # Fixme: after ? self._parser.parse(text, lexer=self._lexer)
# return self._program ##############################################
[docs] def test_lexer(self, text): self._reset() self._lexer.input(text) while True: token = self._lexer.token() if not token: break print(token)