def get_bounds(type_range): ''' Takes a type parsed by inner_vhdl_module and returns math_parser expressions for the upper and lower bounds. ''' if type_range is None: upper = None lower = None else: if type_range.direction == 'to': upper = type_range.right lower = type_range.left elif type_range.direction == 'downto': upper = type_range.left lower = type_range.right else: assert(type_range.left is None) assert(type_range.right is None) upper = None lower = None if (upper is None) and (lower is None): upper_expression = None lower_expression = None else: upper_expression = math_parser.parse_and_simplify(upper) lower_expression = math_parser.parse_and_simplify(lower) return lower_expression, upper_expression
def get_constraint_bounds(constraint): ''' Takes a VHDL constraint and return math_parser expressions for the lower and upper bounds. >>> get_constraint_bounds(' (3+6 to 7) ') (9, 7) >>> get_constraint_bounds('(26 downto 10*2 )') (20, 26) ''' _constrained_range_re = re.compile(r""" \s*\( \s*(?P<range_left>.+?) \s+(?P<direction>to|downto)\s+ (?P<range_right>.+?)\s* \)\s*""", re.MULTILINE | re.IGNORECASE | re.VERBOSE | re.DOTALL) match = _constrained_range_re.match(constraint) if match: gd = match.groupdict() if gd['direction'] == 'to': high = gd['range_right'] low = gd['range_left'] elif gd['direction'] == 'downto': high = gd['range_left'] low = gd['range_right'] high_expr = math_parser.parse_and_simplify(high) low_expr = math_parser.parse_and_simplify(low) size_as_string = '{} + 1 - {}'.format(high, low) size = math_parser.parse_and_simplify(size_as_string) else: raise Exception('Failed to parse constraint.') return low_expr, high_expr
def get_range_bounds(type_range): ''' Takes a VHDL range and return math_parser expressions for the lower and upper bounds. >>> get_range_bounds('range 17 to 201') (17, 201) >>> get_range_bounds('range 7*2 downto 7*1') (7, 14) ''' _type_range_re = re.compile(r""" \s*range \s*(?P<range_left>.+?) \s+(?P<direction>to|downto)\s+ (?P<range_right>.+?)\s*$ """, re.MULTILINE | re.IGNORECASE | re.VERBOSE | re.DOTALL) match = _type_range_re.match(type_range) if match: gd = match.groupdict() if gd['direction'] == 'to': high = gd['range_right'] low = gd['range_left'] elif gd['direction'] == 'downto': high = gd['range_left'] low = gd['range_right'] high_expr = math_parser.parse_and_simplify(high) low_expr = math_parser.parse_and_simplify(low) else: raise Exception('Failed to parse constraint.') return low_expr, high_expr
def process_parsed_package(parsed_package, parsed_uses): ''' Process a parsed package (from inner_vhdl_parser) into an UnresolvedPackage object. ''' p_constants = parsed_package.constants p_types = get_types(parsed_package) constants = {} for constant in p_constants: if constant.text == '': # This typically happens when a parameters file has been generated # incorrectly. raise Exception('Constant {} has no value to parse'.format( constant.identifier)) constants[constant.identifier] = math_parser.parse_and_simplify( constant.text) processed_types = [(t.identifier, typ_parser.process_parsed_type(t)) for t in p_types] # Filter out the types that could not be processed. types = dict([(k, v) for k, v in processed_types if v is not None]) failed_type_keys = [k for k, v in processed_types if v is None] if failed_type_keys: logger.warning('Failed to parse types %s', str(failed_type_keys)) processed_package = package.UnresolvedPackage( identifier=parsed_package.identifier, types=types, constants=constants, uses=parsed_uses, ) return processed_package
def test_substitute(): string = 'fish + 3 * bear * shark / house' simplified = sm.parse_and_simplify(string) substituted = sm.make_substitute_function({ 'fish': 2, 'bear': 4, 'shark': 3, 'house': 2, })(simplified) final = sm.simplify(substituted) assert final == 2 + 3 * 4 * 3 / 2
def get_constraint_size(constraint): ''' Takes a vhdl constraint and return a math_parser expression for the size. >>> get_constraint_size(' (FISH*2-1 downto FISH )') 'FISH' >>> get_constraint_size('(FISH*5-1 downto 0)').str_expression() '5*FISH' ''' low, high = get_constraint_bounds(constraint) size = math_parser.parse_and_simplify('{} + 1 - {}'.format( math_parser.str_expression(high), math_parser.str_expression(low))) return size
def test_simplifications(): ins_and_outs = ( ('fish + 8*bear + 2 * (fish - bear)', ('(3*fish+6*bear)', '(6*bear+3*fish)')), ('4 + (4 - 4) * 2 - 3', ('1', )), ('7 * 7', ('49', )), ('2 * (3 + 5)', ('16', )), ('logceil(5+3)-2', ('1', )), ('1 + 1', ('2', )), ('(logceil(5*4)-1)+1-0', ('5', )), ('3 * 2 / fish / (3 / 4)', ('8/fish', '8*1/fish')), ('fish + 1 - 1', ('fish', )), ('(fish + 1) - 1', ('fish', )), ('fish + 2 * fish', ('3*fish', )), ) for in_string, expected_strings in ins_and_outs: simplified = sm.parse_and_simplify(in_string) out_string = sm.str_expression(simplified) assert out_string in expected_strings
def test_fails_on_power(): string = '2 ** 6' with pytest.raises(sm.MathParsingError) as e: simplified = sm.parse_and_simplify(string) message = e.value.args[0] assert all([s in message for s in ('**', 'power')])
def test_empty_constant_list(): string = '3 * 12' simplified = sm.parse_and_simplify(string) constants = sm.get_constant_list(simplified) assert constants == set()
def test_constant_list(): string = '3 * (fish + 6) - 2 * bear - fish' simplified = sm.parse_and_simplify(string) constants = sm.get_constant_list(simplified) assert constants == set(['fish', 'bear'])