xls icon indicating copy to clipboard operation
xls copied to clipboard

DSLX: Rust-style attributes

Open grebe opened this issue 2 years ago • 0 comments

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:

  1. 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)].
  2. 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.
  3. 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.

grebe avatar Sep 01 '22 19:09 grebe