forked from opendacs/PyHCL
dsl: add io (#28)
This commit is contained in:
parent
0785010acc
commit
3f91cbf6bd
|
@ -0,0 +1,19 @@
|
|||
import logging
|
||||
|
||||
logging.basicConfig(format='%(asctime)s %(message)s')
|
||||
|
||||
|
||||
class PyHclError(BaseException):
|
||||
errs = {}
|
||||
|
||||
@classmethod
|
||||
def append(cls, errs):
|
||||
PyHclError.errs[cls] = errs
|
||||
|
||||
@classmethod
|
||||
def err(cls, name, msg):
|
||||
e = PyHclError.errs[cls][name]
|
||||
ev = e['value']
|
||||
|
||||
logging.error("[%d]: %s", e['code'], msg)
|
||||
return ev
|
|
@ -1,2 +1,5 @@
|
|||
class PyHclError(BaseException):
|
||||
from .. import PyHclError
|
||||
|
||||
|
||||
class CoreError(PyHclError):
|
||||
pass
|
||||
|
|
|
@ -1,16 +1,23 @@
|
|||
from ...core import PyHclError
|
||||
from .. import CoreError
|
||||
|
||||
|
||||
class ModuleErr(PyHclError):
|
||||
pass
|
||||
class ModuleError(CoreError):
|
||||
@staticmethod
|
||||
def not_contains_io(msg):
|
||||
return ModuleError.err('NotContainsIO', msg)
|
||||
|
||||
@staticmethod
|
||||
def duplicate_name(msg):
|
||||
return ModuleError.err('InheritDuplicateName', msg)
|
||||
|
||||
|
||||
err = {
|
||||
# Error Name | Error value | Error Code
|
||||
'NotContainsIO': (ModuleErr('not contains io'), 0),
|
||||
'DuplicateName': (ModuleErr('duplicate names'), 1),
|
||||
}
|
||||
ModuleError.append({
|
||||
'NotContainsIO': {
|
||||
'code': 100,
|
||||
'value': ModuleError('the module lack of io attribute')},
|
||||
|
||||
|
||||
def module_err(name):
|
||||
return err[name][0]
|
||||
'InheritDuplicateName': {
|
||||
'code': 101,
|
||||
'value': ModuleError('modules with inherited relationships '
|
||||
'contain duplicate attributes')},
|
||||
})
|
||||
|
|
|
@ -1,15 +1,20 @@
|
|||
from . import module_err
|
||||
from ...dsl.expression import Expression
|
||||
from py_hcl.core.module_constructor import ModuleError
|
||||
from py_hcl.dsl.expr.expression import Expression
|
||||
|
||||
|
||||
def extract(dct):
|
||||
def extract(dct, name):
|
||||
res = {}
|
||||
|
||||
for k in dct:
|
||||
if isinstance(dct[k], Expression):
|
||||
res[k] = dct[k]
|
||||
for k, v in dct.items():
|
||||
if isinstance(v, Expression):
|
||||
res[k] = v
|
||||
|
||||
if 'io' not in res:
|
||||
raise module_err('NotContainsIO')
|
||||
check_io_exist(res, name)
|
||||
|
||||
return res
|
||||
|
||||
|
||||
def check_io_exist(res, name):
|
||||
if 'io' not in res:
|
||||
raise ModuleError.not_contains_io(
|
||||
'module {} lack of io attribute'.format(name))
|
||||
|
|
|
@ -1,22 +1,50 @@
|
|||
from ..module_constructor import module_err
|
||||
from py_hcl.core.module_constructor import ModuleError
|
||||
from py_hcl.dsl.expr.io import IO
|
||||
|
||||
|
||||
def merge_expr(dest, src):
|
||||
def merge_expr(dest, src, mod_names):
|
||||
io_dest = dest['io']
|
||||
io_src = src['io']
|
||||
io_dest = merge_io(io_dest, io_src)
|
||||
assert isinstance(io_dest, IO)
|
||||
assert isinstance(io_src, IO)
|
||||
|
||||
a = set(dest.keys()) & set(src.keys())
|
||||
if len(a) > 1:
|
||||
raise module_err('DuplicateName')
|
||||
check_dup_mod(dest, src, mod_names)
|
||||
|
||||
io_dest = merge_io(io_dest, io_src, mod_names)
|
||||
res = {**dest, **src, 'io': io_dest}
|
||||
return res
|
||||
|
||||
|
||||
def merge_io(dest, src):
|
||||
return dest, src # TODO
|
||||
def check_dup_mod(dest, src, mod_names):
|
||||
a = set(dest.keys()) & set(src.keys())
|
||||
a.discard('io')
|
||||
if len(a) > 0:
|
||||
dest_name = mod_names[0]
|
||||
src_name = mod_names[1]
|
||||
raise ModuleError.duplicate_name(
|
||||
'module {} has duplicates with {} in '
|
||||
'module {}'.format(dest_name, list(a), src_name)
|
||||
)
|
||||
|
||||
|
||||
def merge_scope(dest, src):
|
||||
def merge_io(dest, src, mod_names):
|
||||
check_dup_io(dest, src, mod_names)
|
||||
dest.ports.extend(src.ports)
|
||||
|
||||
return dest
|
||||
|
||||
|
||||
def check_dup_io(dest, src, mod_names):
|
||||
names = set(p['name'] for p in dest.ports)
|
||||
for p in src.ports:
|
||||
if p['name'] in names:
|
||||
dest_name = mod_names[0]
|
||||
src_name = mod_names[1]
|
||||
raise ModuleError.duplicate_name(
|
||||
'module {} has duplicates with {} in '
|
||||
'module {} in io'.format(dest_name, p['name'], src_name)
|
||||
)
|
||||
|
||||
|
||||
def merge_scope(dest, src, mod_names):
|
||||
return dest, src # TODO
|
||||
|
|
|
@ -5,5 +5,5 @@ class MetaModule(type):
|
|||
def __init__(cls, name, bases, dct):
|
||||
super().__init__(name, bases, dct)
|
||||
|
||||
packed = pack(name, bases, dct)
|
||||
packed = pack(bases, dct, name)
|
||||
cls.packed_module = packed
|
||||
|
|
|
@ -3,17 +3,18 @@ from .extractor import extract
|
|||
from .packed_module import PackedModule
|
||||
|
||||
|
||||
def pack(name, bases, dct):
|
||||
raw_expr = extract(dct)
|
||||
def pack(bases, dct, name):
|
||||
raw_expr = extract(dct, name)
|
||||
raw_scope = ... # TODO
|
||||
|
||||
named_expression, top_scope = handle_inherit(bases, raw_expr, raw_scope)
|
||||
named_expression, top_scope = \
|
||||
handle_inherit(bases, raw_expr, raw_scope, name)
|
||||
|
||||
res = PackedModule(name, named_expression, top_scope)
|
||||
return res
|
||||
|
||||
|
||||
def handle_inherit(bases, named_expression, top_scope):
|
||||
def handle_inherit(bases, named_expression, top_scope, name):
|
||||
for b in bases:
|
||||
if not hasattr(b, 'packed_module'):
|
||||
continue
|
||||
|
@ -22,7 +23,7 @@ def handle_inherit(bases, named_expression, top_scope):
|
|||
expr = pm.named_expressions
|
||||
ts = pm.top_scope
|
||||
|
||||
named_expression = merge_expr(named_expression, expr)
|
||||
top_scope = merge_scope(top_scope, ts)
|
||||
named_expression = merge_expr(named_expression, expr, (name, pm.name))
|
||||
top_scope = merge_scope(top_scope, ts, (name, pm.name))
|
||||
|
||||
return named_expression, top_scope
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
from py_hcl import PyHclError
|
||||
|
||||
|
||||
class DslError(PyHclError):
|
||||
pass
|
|
@ -0,0 +1,14 @@
|
|||
from .. import DslError
|
||||
|
||||
|
||||
class ExprError(DslError):
|
||||
@staticmethod
|
||||
def io_value(msg):
|
||||
return ExprError.err('IOValueError', msg)
|
||||
|
||||
|
||||
ExprError.append({
|
||||
'IOValueError': {
|
||||
'code': 200,
|
||||
'value': ExprError('io items should wrap with Input or Output')},
|
||||
})
|
|
@ -0,0 +1,34 @@
|
|||
from py_hcl.dsl.expr import ExprError
|
||||
from py_hcl.dsl.expr.expression import Expression
|
||||
|
||||
|
||||
class IO(Expression):
|
||||
def __init__(self, **named_ports):
|
||||
self.ports = IO.handle_args(named_ports)
|
||||
|
||||
@staticmethod
|
||||
def handle_args(named_ports):
|
||||
res = []
|
||||
for k, v in named_ports.items():
|
||||
if isinstance(v, Input):
|
||||
res.append({'name': k, 'direct': 'in', 'tpe': v.tpe})
|
||||
continue
|
||||
|
||||
if isinstance(v, Output):
|
||||
res.append({'name': k, 'direct': 'out', 'tpe': v.tpe})
|
||||
continue
|
||||
|
||||
raise ExprError.io_value(
|
||||
"type of '{}' is {}, not Input or Output".format(k, type(v)))
|
||||
|
||||
return res
|
||||
|
||||
|
||||
class Input(Exception):
|
||||
def __init__(self, tpe):
|
||||
self.tpe = tpe
|
||||
|
||||
|
||||
class Output(Exception):
|
||||
def __init__(self, tpe):
|
||||
self.tpe = tpe
|
|
@ -1,8 +1,9 @@
|
|||
from py_hcl.dsl.expr.io import IO
|
||||
from ..core.module_constructor.meta_module import MetaModule
|
||||
from .expression import Expression
|
||||
from py_hcl.dsl.expr.expression import Expression
|
||||
|
||||
|
||||
class Module(metaclass=MetaModule):
|
||||
io = Expression() # TODO
|
||||
io = IO() # TODO
|
||||
clock = Expression() # TODO
|
||||
reset = Expression() # TODO
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
import pytest
|
||||
|
||||
from py_hcl.dsl.expr import ExprError
|
||||
from py_hcl.dsl.expr.io import IO, Input, Output
|
||||
from py_hcl.dsl.module import Module
|
||||
from py_hcl.firrtl_ir.expr import Expression
|
||||
|
||||
|
||||
def test_io():
|
||||
class A(Module):
|
||||
io = IO(
|
||||
i=Input(Expression()),
|
||||
o=Output(Expression()))
|
||||
|
||||
ps = A.packed_module.named_expressions['io'].ports
|
||||
assert len(ps) == 2
|
||||
|
||||
|
||||
def test_io_no_wrap_io():
|
||||
with pytest.raises(ExprError, match='^.*Input.*Output.*$'):
|
||||
class A(Module):
|
||||
io = IO(i=Expression())
|
||||
|
||||
with pytest.raises(ExprError, match='^.*Input.*Output.*$'):
|
||||
class A(Module): # noqa: F811
|
||||
io = IO(
|
||||
i=Expression(),
|
||||
o=Output(Expression()))
|
||||
|
||||
with pytest.raises(ExprError, match='^.*Input.*Output.*$'):
|
||||
class A(Module): # noqa: F811
|
||||
io = IO(
|
||||
i=Input(Expression()),
|
||||
o=Expression())
|
|
@ -1,13 +1,14 @@
|
|||
import pytest
|
||||
|
||||
from py_hcl.core.module_constructor import ModuleErr
|
||||
from py_hcl.dsl.expression import Expression
|
||||
from py_hcl.core.module_constructor import ModuleError
|
||||
from py_hcl.dsl.expr.expression import Expression
|
||||
from py_hcl.dsl.expr.io import IO, Input
|
||||
from py_hcl.dsl.module import Module
|
||||
|
||||
|
||||
def test_module():
|
||||
class A(Module):
|
||||
io = Expression()
|
||||
io = IO()
|
||||
a = Expression()
|
||||
|
||||
assert hasattr(A, "packed_module")
|
||||
|
@ -16,18 +17,18 @@ def test_module():
|
|||
|
||||
|
||||
def test_module_not_contains_io():
|
||||
with pytest.raises(ModuleErr, match='not contains io'):
|
||||
with pytest.raises(ModuleError, match='^.*lack of io.*$'):
|
||||
class A(Module):
|
||||
b = Expression()
|
||||
|
||||
|
||||
def test_module_inherit():
|
||||
class A(Module):
|
||||
io = Expression()
|
||||
io = IO()
|
||||
a = Expression()
|
||||
|
||||
class B(A):
|
||||
io = Expression()
|
||||
io = IO()
|
||||
b = Expression()
|
||||
|
||||
assert hasattr(B, "packed_module")
|
||||
|
@ -36,11 +37,22 @@ def test_module_inherit():
|
|||
|
||||
|
||||
def test_module_duplicate_name():
|
||||
with pytest.raises(ModuleErr, match='duplicate names'):
|
||||
with pytest.raises(ModuleError, match='^.*duplicate.*$'):
|
||||
class A(Module):
|
||||
io = Expression()
|
||||
io = IO()
|
||||
a = Expression()
|
||||
|
||||
class B(A):
|
||||
io = Expression()
|
||||
io = IO()
|
||||
a = Expression()
|
||||
|
||||
|
||||
def test_module_io_duplicate_name():
|
||||
with pytest.raises(ModuleError, match='^.*duplicate.*$'):
|
||||
class A(Module):
|
||||
io = IO(i=Input(Expression()))
|
||||
a = Expression()
|
||||
|
||||
class B(A):
|
||||
io = IO(i=Input(Expression()))
|
||||
a = Expression()
|
||||
|
|
Loading…
Reference in New Issue