# coding=utf-8
#This file is part of QNET.
#
# QNET 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.
#
# QNET 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 QNET. If not, see <http://www.gnu.org/licenses/>.
#
# Copyright (C) 2012-2013, Nikolas Tezak
#
###########################################################################
"""
The PLY-based QHDLParser class.
"""
from qnet.misc.parser import Parser, ParsingError
from qnet.qhdl import qhdl
[docs]class QHDLParser(Parser):
[docs] def parse(self, inputstring):
self.entities = {}
self.architectures = {}
return Parser.parse(self, inputstring)
[docs] def create_circuit_lib(self, arch_id = None):
if arch_id == None:
if len(self.architectures) > 1:
raise Exception('Multiple architectures found: %s. \
Please provide an arch_id argument selecting one architecture' % \
", ".join(self.architectures.keys()))
arch_id = self.architectures.keys().pop()
arch = self.architectures[arch_id]
arch_circuit = arch.to_circuit()
print(arch_circuit)
reserved = {
'begin': 'BEGIN',
'end': 'END',
'entity': 'ENTITY',
'component': 'COMPONENT',
'architecture': 'ARCHITECTURE',
'of': 'OF',
'is': 'IS',
'port': 'PORT',
'in': 'IN',
'out': 'OUT',
'inout': 'INOUT',
'fieldmode': 'FIELDMODE',
'lossy_fieldmode': 'LOSSY_FIELDMODE',
'generic': 'GENERIC',
'signal': 'SIGNAL',
'map': 'MAP',
'real': 'REAL',
'complex': 'COMPLEX',
'int': 'INT'
}
tokens = list(reserved.values()) + [
# Literals (identifier, integer constant, float constant, string constant, char const)
'ID',
'ICONST', 'FCONST',
# Assignment ( :=, =>, <=)
'ASSIGN', 'FEEDRIGHT','FEEDLEFT',
# Delimeters ( ) , ; :
'LPAREN', 'RPAREN',
'COMMA', 'SEMI', 'COLON',
]
def __init__(self, **kw):
Parser.__init__(self, **kw)
self.entities = {}
self.architectures = {}
t_ignore = ' \t\x0c'
# Newlines
[docs] def t_NEWLINE(self, t):
r'\n+'
t.lexer.lineno += t.value.count("\n")
# Assignment operators
t_ASSIGN = r':='
t_FEEDRIGHT = '=>'
t_FEEDLEFT = '<='
# Delimeters
t_LPAREN = r'\('
t_RPAREN = r'\)'
t_COMMA = r','
t_SEMI = r';'
t_COLON = r':'
# Identifiers and reserved words
[docs] def t_ID(self, t):
r"""[_A-Za-z][\w_]*"""
t.type = self.reserved.get(t.value.lower(),"ID")
return t
# Integer literal
t_ICONST = r'-?\d+' #([uU]|[lL]|[uU][lL]|[lL][uU])?'
# Floating literal
t_FCONST = r'-?((\d+)(\.\d+)(e(\+|-)?(\d+))? | (\d+)e(\+|-)?(\d+))' #([lL]|[fF])?'
# Comments
[docs] def t_error(self, t):
print("Illegal character %s" % repr(t.value[0]))
t.lexer.skip(1)
start = 'top_level_list'
[docs] def p_top_level_list(self, p):
"""
top_level_list : top_level_list top_level_unit
| top_level_unit
"""
if len(p) == 2:
if isinstance(p[1], qhdl.Architecture):
p[0] = {'architectures':{p[1].identifier: p[1]}, 'entities': {}}
elif isinstance(p[1], qhdl.Entity):
p[0] = {'entities':{p[1].identifier: p[1]}, 'architectures': {}}
else:
raise ParsingError()
else:
if isinstance(p[2], qhdl.Architecture):
assert p[2].identifier not in p[1]['architectures']
p[1]['architectures'][p[2].identifier] = p[2]
p[0] = p[1]
elif isinstance(p[1], qhdl.Entity):
assert p[2].identifier not in p[1]['entities']
p[1]['entities'][p[2].identifier] = p[2]
p[0] = p[1]
else:
raise ParsingError()
[docs] def p_top_level_unit(self, p):
"""
top_level_unit : entity_declaration
| architecture_declaration
"""
p[0] = p[1]
[docs] def p_entity_declaration(self, p):
"""
entity_declaration : ENTITY ID IS generic_clause port_clause END opt_entity opt_id SEMI
"""
identifier = p[2]
second_identifer = p[8]
generics = p[4]
ports = p[5]
if second_identifer != None and identifier != second_identifer:
raise ParsingError('IDs don\'t match: %s, %s' % (identifier, second_identifer))
p[0] = self.entities[identifier] = qhdl.Entity(identifier, generics, ports)
[docs] def p_opt_entity(self, p):
"""
opt_entity : ENTITY
| empty
"""
p[0] = p[1]
[docs] def p_opt_id(self, p):
"""
opt_id : ID
| empty
"""
p[0] = p[1]
[docs] def p_opt_semi(self, p):
"""
opt_semi : SEMI
| empty
"""
pass
[docs] def p_generic_clause(self, p):
"""
generic_clause : generic_statement
| empty
"""
if p[1] == None:
p[0] = []
else:
p[0] = p[1]
[docs] def p_empty(self, p):
""" empty : """
p[0] = None
[docs] def p_generic_statement(self, p):
"""
generic_statement : GENERIC LPAREN generic_list opt_semi RPAREN SEMI
"""
p[0] = p[3]
[docs] def p_generic_list(self, p):
"""
generic_list : generic_list SEMI generic_entry_group
| generic_entry_group
"""
if len(p) == 2:
p[0] = [p[1]]
else:
p[0] = p[1] + [p[3]]
[docs] def p_generic_entry_group(self, p):
"""
generic_entry_group : id_list COLON generic_type generic_default
"""
p[0] = p[1], p[3], p[4]
[docs] def p_id_list(self, p):
"""
id_list : id_list COMMA ID
| ID
"""
if len(p) == 2:
p[0] = [p[1]]
else:
p[0] = p[1] + [p[3]]
[docs] def p_generic_type(self, p):
""" generic_type : REAL
| COMPLEX
| INT """
p[0] = p[1]
[docs] def p_generic_default(self, p):
"""
generic_default : ASSIGN number
| empty
"""
if len(p) == 3:
p[0] = p[2]
else:
p[0] = None
[docs] def p_number(self, p):
"""
number : simple_number
| complex
"""
p[0] = p[1]
[docs] def p_simple_number(self, p):
"""
simple_number : int
| real
"""
p[0] = p[1]
[docs] def p_int(self, p):
"""
int : ICONST
"""
p[0] = int(p[1])
[docs] def p_real(self, p):
"""
real : FCONST
"""
p[0] = float(p[1])
[docs] def p_complex(self, p):
"""
complex : LPAREN simple_number COMMA simple_number RPAREN
"""
p[0] = complex(float(p[2]), float(p[4]))
[docs] def p_port_clause(self, p):
"""
port_clause : port_statement
| empty
"""
if p[1] == None:
p[0] = []
else:
p[0] = p[1]
[docs] def p_port_statement(self, p):
"""
port_statement : PORT LPAREN port_list opt_semi RPAREN SEMI
"""
p[0] = p[3]
[docs] def p_port_list(self, p):
"""
port_list : with_io_port_list
| non_io_port_list
"""
# if len(p) == 2:
p[0] = p[1]
# else:
# p[0] = p[1] + [p[3]]
[docs] def p_with_io_port_list(self, p):
"""
with_io_port_list : io_port_entry_group SEMI non_io_port_list
| io_port_entry_group
"""
if len(p) == 2:
p[0] = [p[1]]
else:
p[0] = [p[1]] + p[3]
[docs] def p_non_io_port_list(self, p):
"""
non_io_port_list : non_io_port_entry_group SEMI non_io_port_list
| non_io_port_entry_group
"""
if len(p) == 2:
p[0] = [p[1]]
else:
p[0] = [p[1]] + p[3]
[docs] def p_non_io_port_entry_group(self, p):
"""
non_io_port_entry_group : id_list COLON signal_direction signal_type
"""
p[0] = p[1], p[3], p[4]
[docs] def p_io_port_entry_group(self, p):
"""
io_port_entry_group : id_list COLON INOUT signal_type
"""
p[0] = p[1], p[3], p[4]
[docs] def p_signal_direction(self, p):
"""
signal_direction : IN
| OUT
"""
p[0] = p[1]
[docs] def p_signal_type(self, p):
"""
signal_type : FIELDMODE
| LOSSY_FIELDMODE
"""
p[0] = p[1]
[docs] def p_architecture_declaration(self, p):
"""
architecture_declaration : ARCHITECTURE ID OF ID IS architecture_head BEGIN instance_mapping_assignment_list feedleft_assignment_list END opt_arch opt_id SEMI
"""
a_id, e_id = p[2], p[4]
component_list, signal_list = p[6]
assignments = p[8]
fl_assignments = p[9]
if p[12] != None and p[12] != a_id:
raise ParsingError('IDs don\'t match!')
entity = self.entities.get(e_id, False)
if not entity:
raise qhdl.QHDLError('Entity %s not found.' % e_id)
p[0] = self.architectures[a_id] = qhdl.Architecture(a_id, entity, component_list, signal_list, assignments, fl_assignments)
[docs] def p_architecture_head(self, p):
"""
architecture_head : component_declaration_list signal_list
"""
p[0] = (p[1], p[2])
[docs] def p_opt_arch(self, p):
"""
opt_arch : ARCHITECTURE
| empty
"""
p[0] = p[1]
[docs] def p_component_declaration_list(self, p):
"""
component_declaration_list : component_declaration_list component_declaration
| component_declaration
"""
if len(p) == 2:
p[0] = [p[1]]
else:
p[0] = p[1] + [p[2]]
[docs] def p_component_declaration(self, p):
"""
component_declaration : COMPONENT ID generic_clause port_clause END COMPONENT opt_id SEMI
"""
if p[7] != None and p[7] != p[2]:
raise ParsingError('IDs don\'t match')
p[0] = qhdl.Component(p[2], p[3], p[4])
[docs] def p_signal_list(self, p):
"""
signal_list : signal_list signal_entry_group
| signal_entry_group
"""
if len(p) == 2:
p[0] = [p[1]]
else:
p[0] = p[1] + [p[2]]
[docs] def p_signal_entry_group(self, p):
"""
signal_entry_group : SIGNAL id_list COLON signal_type SEMI
"""
p[0] = p[2], p[4]
[docs] def p_instance_mapping_assignment_list(self, p):
"""
instance_mapping_assignment_list : instance_mapping_assignment_list instance_mapping_assignment
| instance_mapping_assignment
"""
if len(p) == 2:
p[0] = [p[1]]
else:
p[0] = p[1] + [p[2]]
[docs] def p_instance_mapping_assignment(self, p):
"""
instance_mapping_assignment : ID COLON ID generic_map port_map
"""
p[0] = (p[1], p[3], p[4], p[5])
[docs] def p_generic_map(self, p):
"""
generic_map : GENERIC MAP LPAREN feedright_generic_assignment_list RPAREN SEMI
| empty
"""
if len(p) == 2:
p[0] = {}
else:
p[0] = p[4]
[docs] def p_feedright_generic_assignment_list(self, p):
"""
feedright_generic_assignment_list : feedright_generic_assignment_list COMMA feedright_generic_assignment
| feedright_generic_assignment
"""
if len(p) == 2:
asm = p[1]
if isinstance(asm, dict):
p[0] = asm
else:
p[0] = [p[1]]
else:
asm_list = p[1]
asm = p[3]
if isinstance(asm, dict):
if not isinstance(asm_list, dict):
raise Exception("Either specify ALL assignments as 'from_id=>to_id|number' OR just as 'to_id|number'.")
if len(set(asm.keys()) & set(asm_list.keys())) > 0:
raise Exception("Every from_id may only occur once!")
asm_list.update(asm)
p[0] = asm_list
else:
if not isinstance(asm_list, list):
raise Exception("Either specify ALL assignments as 'from_id=>to_id|number' OR just as 'to_id|number'.")
p[0] = p[1] + [p[3]]
[docs] def p_id_or_value(self, p):
"""
id_or_value : ID
| number
"""
p[0] = p[1]
[docs] def p_feedright_generic_assignment(self, p):
"""
feedright_generic_assignment : ID FEEDRIGHT id_or_value
| id_or_value
"""
if len(p) == 2:
p[0] = p[1]
else:
p[0] = {p[1]: p[3]}
[docs] def p_feedright_port_assignment_list(self, p):
"""
feedright_port_assignment_list : feedright_port_assignment_list COMMA feedright_port_assignment
| feedright_port_assignment
"""
if len(p) == 2:
asm = p[1]
if isinstance(asm, dict):
p[0] = asm
else:
p[0] = [p[1]]
else:
asm_list = p[1]
asm = p[3]
if isinstance(asm, dict):
if not isinstance(asm_list, dict):
raise Exception("Either specify ALL assignments as 'from_id=>to_id' OR just as 'to_id'.")
if len(set(asm.keys()) & set(asm_list.keys())) > 0:
raise Exception("Every from_id may only occur once!")
asm_list.update(asm)
p[0] = asm_list
else:
if not isinstance(asm_list, list):
raise Exception("Either specify ALL assignments as 'from_id=>to_id' OR just as 'to_id'.")
p[0] = p[1] + [p[3]]
[docs] def p_feedright_port_assignment(self, p):
"""
feedright_port_assignment : ID FEEDRIGHT ID
| ID
"""
if len(p) == 2:
p[0] = p[1]
else:
p[0] = {p[1]: p[3]}
[docs] def p_port_map(self, p):
"""
port_map : PORT MAP LPAREN feedright_port_assignment_list RPAREN SEMI
| empty
"""
if len(p) == 2:
p[0] = {}
else:
p[0] = p[4]
[docs] def p_feedleft_assignment_list(self, p):
"""
feedleft_assignment_list : feedleft_assignment_list feedleft_assignment
| feedleft_assignment
"""
if len(p) == 2:
p[0] = p[1]
else:
dkeys = set(p[1].keys()) & set(p[2].keys())
if len(dkeys) != 0:
raise Exception("Duplicate assignment of global identifiers %s" % (tuple(dkeys),))
p[1].update(p[2])
p[0] = p[1]
[docs] def p_feedleft_assignment(self, p):
"""
feedleft_assignment : ID FEEDLEFT ID SEMI
| empty
"""
if len(p) == 2:
p[0] = {}
else:
p[0] = {p[3]:p[1]}
[docs] def p_error(self, p):
print(p, self.lexer.lineno)
raise ParsingError()