def __exit__(self, *args):
self._append("{}end\n", " " * self.indent)
- def case(self, *values):
+ def case(self, *values, attrs={}, src=""):
+ self._attributes(attrs, src=src, indent=self.indent + 1)
if values == ():
self._append("{}case\n", " " * (self.indent + 1))
else:
self._has_rhs = False
@contextmanager
- def case(self, switch, values):
+ def case(self, switch, values, src=""):
try:
old_case = self._case
- with switch.case(*values) as self._case:
+ with switch.case(*values, src=src) as self._case:
yield
finally:
self._case = old_case
with self._case.switch(test_sigspec, src=src(stmt.src_loc)) as switch:
for values, stmts in stmt.cases.items():
- with self.case(switch, values):
+ if values in stmt.case_src_locs:
+ case_src = src(stmt.case_src_locs[values])
+ else:
+ case_src = ""
+ with self.case(switch, values, src=case_src):
self.on_statements(stmts)
def on_statement(self, stmt):
# @final
class Switch(Statement):
- def __init__(self, test, cases, *, src_loc=None, src_loc_at=0):
+ def __init__(self, test, cases, *, src_loc=None, src_loc_at=0, case_src_locs={}):
if src_loc is None:
super().__init__(src_loc_at=src_loc_at)
else:
# Switch is a bit special in terms of location tracking because it is usually created
# long after the control has left the statement that directly caused its creation.
self.src_loc = src_loc
+ # Switch is also a bit special in that its parts also have location information. It can't
+ # be automatically traced, so whatever constructs a Switch may optionally provide it.
+ self.case_src_locs = {}
self.test = Value.wrap(test)
self.cases = OrderedDict()
- for keys, stmts in cases.items():
+ for orig_keys, stmts in cases.items():
# Map: None -> (); key -> (key,); (key...) -> (key...)
+ keys = orig_keys
if keys is None:
keys = ()
if not isinstance(keys, tuple):
if not isinstance(stmts, Iterable):
stmts = [stmts]
self.cases[new_keys] = Statement.wrap(stmts)
+ if orig_keys in case_src_locs:
+ self.case_src_locs[new_keys] = case_src_locs[orig_keys]
def _lhs_signals(self):
signals = union((s._lhs_signals() for ss in self.cases.values() for s in ss),
@contextmanager
def If(self, cond):
self._check_context("If", context=None)
+ src_loc = tracer.get_src_loc(src_loc_at=1)
if_data = self._set_ctrl("If", {
- "tests": [],
- "bodies": [],
- "src_loc": tracer.get_src_loc(src_loc_at=1),
+ "tests": [],
+ "bodies": [],
+ "src_loc": src_loc,
+ "src_locs": [],
})
try:
_outer_case, self._statements = self._statements, []
self._flush_ctrl()
if_data["tests"].append(cond)
if_data["bodies"].append(self._statements)
+ if_data["src_locs"].append(src_loc)
finally:
self.domain._depth -= 1
self._statements = _outer_case
@contextmanager
def Elif(self, cond):
self._check_context("Elif", context=None)
+ src_loc = tracer.get_src_loc(src_loc_at=1)
if_data = self._get_ctrl("If")
if if_data is None:
raise SyntaxError("Elif without preceding If")
self._flush_ctrl()
if_data["tests"].append(cond)
if_data["bodies"].append(self._statements)
+ if_data["src_locs"].append(src_loc)
finally:
self.domain._depth -= 1
self._statements = _outer_case
@contextmanager
def Else(self):
self._check_context("Else", context=None)
+ src_loc = tracer.get_src_loc(src_loc_at=1)
if_data = self._get_ctrl("If")
if if_data is None:
raise SyntaxError("Else without preceding If/Elif")
yield
self._flush_ctrl()
if_data["bodies"].append(self._statements)
+ if_data["src_locs"].append(src_loc)
finally:
self.domain._depth -= 1
self._statements = _outer_case
"test": Value.wrap(test),
"cases": OrderedDict(),
"src_loc": tracer.get_src_loc(src_loc_at=1),
+ "case_src_locs": {},
})
try:
self._ctrl_context = "Switch"
@contextmanager
def Case(self, *values):
self._check_context("Case", context="Switch")
+ src_loc = tracer.get_src_loc(src_loc_at=1)
switch_data = self._get_ctrl("Switch")
new_values = ()
for value in values:
# which means the branch will always match.
if not (values and not new_values):
switch_data["cases"][new_values] = self._statements
+ switch_data["case_src_locs"][new_values] = src_loc
finally:
self._ctrl_context = "Switch"
self._statements = _outer_case
"decoding": OrderedDict(),
"states": OrderedDict(),
"src_loc": tracer.get_src_loc(src_loc_at=1),
+ "state_src_locs": {},
})
self._generated[name] = fsm = \
FSM(fsm_data["signal"], fsm_data["encoding"], fsm_data["decoding"])
@contextmanager
def State(self, name):
self._check_context("FSM State", context="FSM")
+ src_loc = tracer.get_src_loc(src_loc_at=1)
fsm_data = self._get_ctrl("FSM")
if name in fsm_data["states"]:
raise SyntaxError("FSM state '{}' is already defined".format(name))
yield
self._flush_ctrl()
fsm_data["states"][name] = self._statements
+ fsm_data["state_src_locs"][name] = src_loc
finally:
self._ctrl_context = "FSM"
self._statements = _outer_case
if name == "If":
if_tests, if_bodies = data["tests"], data["bodies"]
+ if_src_locs = data["src_locs"]
tests, cases = [], OrderedDict()
for if_test, if_case in zip(if_tests + [None], if_bodies):
match = None
cases[match] = if_case
- self._statements.append(Switch(Cat(tests), cases, src_loc=src_loc))
+ self._statements.append(Switch(Cat(tests), cases,
+ src_loc=src_loc, case_src_locs=dict(zip(cases, if_src_locs))))
if name == "Switch":
switch_test, switch_cases = data["test"], data["cases"]
+ switch_case_src_locs = data["case_src_locs"]
- self._statements.append(Switch(switch_test, switch_cases, src_loc=src_loc))
+ self._statements.append(Switch(switch_test, switch_cases,
+ src_loc=src_loc, case_src_locs=switch_case_src_locs))
if name == "FSM":
fsm_signal, fsm_reset, fsm_encoding, fsm_decoding, fsm_states = \
data["signal"], data["reset"], data["encoding"], data["decoding"], data["states"]
+ fsm_state_src_locs = data["state_src_locs"]
if not fsm_states:
return
fsm_signal.nbits = bits_for(len(fsm_encoding) - 1)
fsm_signal.decoder = lambda n: "{}/{}".format(fsm_decoding[n], n)
self._statements.append(Switch(fsm_signal,
OrderedDict((fsm_encoding[name], stmts) for name, stmts in fsm_states.items()),
- src_loc=src_loc))
+ src_loc=src_loc, case_src_locs={fsm_encoding[name]: fsm_state_src_locs[name]
+ for name in fsm_states}))
def _add_statement(self, assigns, domain, depth, compat_mode=False):
def domain_name(domain):