0

In Scala 3 (3.1.3 right now, if it matters), I'm writing a parser that requires some rule/grammar processing and I'd like to write a macro that lets me define the alternates for a non-terminal and define the non-terminal so that it's available for use in other rules. Everything is written now with the non-terminals as Strings and the terminals as Chars, so a set of rules for a grammar of tuples of as might be:

new Grammar() {
  val rules = Set(
    Rule("tuple", List('(', "as", ')')),
    Rule("as", List('a', "more")),
    Rule("as", Nil),
    Rule("more", List(',', "as")),
    Rule("more", Nil),
  )

What I'd like to do instead is use some macro magic to make it more like:

new Grammar() {
  rule(tuple, List('(', as, ')')
  rule(as, List('a', more), Nil)
  rule(more, List(',', as), Nil)
}

where, instead of using Strings for non-terminals, I could use identifiers, and at compile time, the rule macro would do something like turn the second into

new Grammar() {
  val rules = mutable.Set()

  val tuple = NonTerminal("tuple")
  val as = NonTerminal("as")
  val more = NonTerminal("more")
  rules.add(Rule(tuple, List('(', as, ')')))
  rules.add(Rule(as, List('a', more)))
  rules.add(Rule(as, Nil))
  rules.add(Rule(more, List(',', as)))
  rules.add(Rule(more, Nil)
}

Is such a thing possible with Scala 3 macros in their current state, or is it not possible for a macro to take a not-yet-defined identifier as an argument and provide a definition for it?

Dmytro Mitin
  • 48,194
  • 3
  • 28
  • 66
Todd O'Bryan
  • 2,234
  • 17
  • 30

1 Answers1

1

No, the argument you pass to the macro must be a correct Scala code.

You can have a 'god-prefix' for non-terminals like (by using Dynamic):

new Grammar() {
  rule(?.tuple, List('(', ?.as, ')')
  rule(?.as, List('a', ?.more), Nil)
  rule(?.more, List(',', ?.as), Nil)
}

But I'm not sure if that's better than Strings.

KacperFKorban
  • 326
  • 1
  • 5
  • At least typos and such would get caught at compile-time, right? – Todd O'Bryan Sep 05 '22 at 00:22
  • Dynamic doesn't allow limiting the set of fields you can use. At least not in an easy way. There's a new feature of Scala 3 called Selectable that allows you to do that, but it would also require some clever design. https://dotty.epfl.ch/api/scala/Selectable.html# – KacperFKorban Sep 06 '22 at 09:21