dsl: add io (#28)

This commit is contained in:
Gaufoo 2019-11-29 20:10:57 +08:00 committed by GitHub
parent 0785010acc
commit 3f91cbf6bd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 210 additions and 47 deletions

View File

@ -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

View File

@ -1,2 +1,5 @@
class PyHclError(BaseException): from .. import PyHclError
class CoreError(PyHclError):
pass pass

View File

@ -1,16 +1,23 @@
from ...core import PyHclError from .. import CoreError
class ModuleErr(PyHclError): class ModuleError(CoreError):
pass @staticmethod
def not_contains_io(msg):
return ModuleError.err('NotContainsIO', msg)
@staticmethod
def duplicate_name(msg):
return ModuleError.err('InheritDuplicateName', msg)
err = { ModuleError.append({
# Error Name | Error value | Error Code 'NotContainsIO': {
'NotContainsIO': (ModuleErr('not contains io'), 0), 'code': 100,
'DuplicateName': (ModuleErr('duplicate names'), 1), 'value': ModuleError('the module lack of io attribute')},
}
'InheritDuplicateName': {
def module_err(name): 'code': 101,
return err[name][0] 'value': ModuleError('modules with inherited relationships '
'contain duplicate attributes')},
})

View File

@ -1,15 +1,20 @@
from . import module_err from py_hcl.core.module_constructor import ModuleError
from ...dsl.expression import Expression from py_hcl.dsl.expr.expression import Expression
def extract(dct): def extract(dct, name):
res = {} res = {}
for k in dct: for k, v in dct.items():
if isinstance(dct[k], Expression): if isinstance(v, Expression):
res[k] = dct[k] res[k] = v
if 'io' not in res: check_io_exist(res, name)
raise module_err('NotContainsIO')
return res 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))

View File

@ -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_dest = dest['io']
io_src = src['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()) check_dup_mod(dest, src, mod_names)
if len(a) > 1:
raise module_err('DuplicateName')
io_dest = merge_io(io_dest, io_src, mod_names)
res = {**dest, **src, 'io': io_dest} res = {**dest, **src, 'io': io_dest}
return res return res
def merge_io(dest, src): def check_dup_mod(dest, src, mod_names):
return dest, src # TODO 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 return dest, src # TODO

View File

@ -5,5 +5,5 @@ class MetaModule(type):
def __init__(cls, name, bases, dct): def __init__(cls, name, bases, dct):
super().__init__(name, bases, dct) super().__init__(name, bases, dct)
packed = pack(name, bases, dct) packed = pack(bases, dct, name)
cls.packed_module = packed cls.packed_module = packed

View File

@ -3,17 +3,18 @@ from .extractor import extract
from .packed_module import PackedModule from .packed_module import PackedModule
def pack(name, bases, dct): def pack(bases, dct, name):
raw_expr = extract(dct) raw_expr = extract(dct, name)
raw_scope = ... # TODO 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) res = PackedModule(name, named_expression, top_scope)
return res return res
def handle_inherit(bases, named_expression, top_scope): def handle_inherit(bases, named_expression, top_scope, name):
for b in bases: for b in bases:
if not hasattr(b, 'packed_module'): if not hasattr(b, 'packed_module'):
continue continue
@ -22,7 +23,7 @@ def handle_inherit(bases, named_expression, top_scope):
expr = pm.named_expressions expr = pm.named_expressions
ts = pm.top_scope ts = pm.top_scope
named_expression = merge_expr(named_expression, expr) named_expression = merge_expr(named_expression, expr, (name, pm.name))
top_scope = merge_scope(top_scope, ts) top_scope = merge_scope(top_scope, ts, (name, pm.name))
return named_expression, top_scope return named_expression, top_scope

View File

@ -0,0 +1,5 @@
from py_hcl import PyHclError
class DslError(PyHclError):
pass

View File

@ -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')},
})

34
py_hcl/dsl/expr/io.py Normal file
View File

@ -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

View File

@ -1,8 +1,9 @@
from py_hcl.dsl.expr.io import IO
from ..core.module_constructor.meta_module import MetaModule from ..core.module_constructor.meta_module import MetaModule
from .expression import Expression from py_hcl.dsl.expr.expression import Expression
class Module(metaclass=MetaModule): class Module(metaclass=MetaModule):
io = Expression() # TODO io = IO() # TODO
clock = Expression() # TODO clock = Expression() # TODO
reset = Expression() # TODO reset = Expression() # TODO

34
tests/test_dsl/test_io.py Normal file
View File

@ -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())

View File

@ -1,13 +1,14 @@
import pytest import pytest
from py_hcl.core.module_constructor import ModuleErr from py_hcl.core.module_constructor import ModuleError
from py_hcl.dsl.expression import Expression from py_hcl.dsl.expr.expression import Expression
from py_hcl.dsl.expr.io import IO, Input
from py_hcl.dsl.module import Module from py_hcl.dsl.module import Module
def test_module(): def test_module():
class A(Module): class A(Module):
io = Expression() io = IO()
a = Expression() a = Expression()
assert hasattr(A, "packed_module") assert hasattr(A, "packed_module")
@ -16,18 +17,18 @@ def test_module():
def test_module_not_contains_io(): 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): class A(Module):
b = Expression() b = Expression()
def test_module_inherit(): def test_module_inherit():
class A(Module): class A(Module):
io = Expression() io = IO()
a = Expression() a = Expression()
class B(A): class B(A):
io = Expression() io = IO()
b = Expression() b = Expression()
assert hasattr(B, "packed_module") assert hasattr(B, "packed_module")
@ -36,11 +37,22 @@ def test_module_inherit():
def test_module_duplicate_name(): def test_module_duplicate_name():
with pytest.raises(ModuleErr, match='duplicate names'): with pytest.raises(ModuleError, match='^.*duplicate.*$'):
class A(Module): class A(Module):
io = Expression() io = IO()
a = Expression() a = Expression()
class B(A): 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() a = Expression()