xls
xls copied to clipboard
DSLX: Rust-style attributes
Right now, we have test
, test_proc
, cfg
, and quickcheck
"directives" which are similar to rust attributes[^1]. These directives result in different ast nodes being parsed, e.g. #[test] fn some_test_function() { ... }
does not parse to a Function
with a test
attribute, it parses to a TestFunction
. In playing around with getting SRAM support working, it seems to me that a version of the latter would be helpful, for example:
#[IOConstraint("req_chan:send", "resp_chan:recv", min_latency=1, max_latency=1)]
proc Sram<ADDR_WIDTH:u32, DATA_WIDTH:u32, SIZE:u32> {
#[ChannelConfig(flow_control="valid_only")]
req_chan: chan in SramReq<ADDR_WIDTH, DATA_WIDTH>;
#[ChannelConfig(flow_control="valid_only")]
resp_chan: chan out SramResp<DATA_WIDTH>;
config(...) { ... }
next(...) { ... }
}
I've played around a bit with what it would look like to add this to the ast+parser, and here are some questions I have:
- I'm being pretty loose w/ what's allowed in the argument list. It would be nice to have named arguments. I could imagine the IO constraint being inside the body and referencing the channels directly, like
#![IOContraint(req_chan, kSend, resp_chan, kRecv, min_latency=1, max_latency=1)]
. - What entities should accept attributes? This seems to be a matter of some debate for rust too. If enough entities have attributes, perhaps an attribute list should be a member of
AstNode
. - Related to 2., should the parser do
list_of_attributes = TryParseAttributes()
if peek_token() == OpA:
return ParseOpA(list_of_attributes)
elif peek_token() == OpB:
return ParseOpB(list_of_attributes)
...
or
def ParseOpA():
list_of_attributes = TryParseAttributes()
The first is needed if you're dispatching on a token (you need to get through the attributes to know what to dispatch on). The second is more natural if you're not dispatching on a token (it encapsulates the op better). It would be nice to be consistent and make the parsing steps more composable.
I suppose you could also do
next_op_token = peek_token_after_attributes()
if next_op_token == OpA:
return ParseOpA()
if next_op_token == OpB:
return ParseOpB()
or similar using transactions and have ParseOpA()
and ParseOpB()
actually parse the attributes. Is scanning forwards and backwards too wasteful?
4. It seems useful to go beyond built-in attribute macros. You could imagine something writing a macro implementation in C++/Python/whatever, and using a DSLX import (or use
statement?) to pull in the macro implementation.
[^1]: As a side-node, it seems that we're incorrectly using inner attributes instead of outer attributes for most of these.