3

I'm toying around with fluent interfaces in the style of Martin Fowlers text, and I'm wondering if the grammar they are describing is context free or regular? I'm talking about interfaces such as this:

var car = new Car();
car.Configure().MakeCar.With.Wheels(4).And.Engine.Using.Petrol;

What I'm trying to do is to write a program which can generate them. Currently it requires an input of a context free grammar, but I seem to have some difficulty in converting this into source code application. I'm suspecting that the answer is that I can only reach for regular grammars since the state of the "stack" cannot be known since the result of each "terminal" method must be known beforehand.

What I've got now works, but it bugs out on certain grammars.

Edit: I went with regular grammars, the code is open sourced and working now if anyone's keen to toy around with it. https://github.com/Dervall/Snout

Dervall
  • 5,736
  • 3
  • 25
  • 48
  • It was supposed to be properties in C#, as an example language. – Dervall Mar 25 '12 at 18:51
  • Indeed, my mistake. I missed the `var` and assumed Java due to the Fowler mention. BTW, neat idea to generate this from CFGs! – Matt Ball Mar 25 '12 at 18:53
  • That's the idea at least, but I think we'll have to settle for regular grammars, or I'm writing buggy code. :) – Dervall Mar 25 '12 at 18:55

1 Answers1

2

the set of options at any point are determined by the methods available on the class at that point. the class returned by that method determines the next set of methods.

so the rules for the grammar that generates the chain is a right regular grammar where the start symbols are classes, the symbols are methods, and the non-terminals are the classes returned by the methods:

class Car:
    configure: Configurator

class Configurator:
    with: Configurator // noise method
    and: Configurator // noise method
    wheels: int -> Configurator
    windows: int -> WindowDetails

class WindowDetails:
    transparent -> Configurator
    tinted -> Configurator

ignoring the method args (int):

Car -> "configure" Configurator
Configurator -> "with" Configurator
Configurator -> "and" Configurator
Configurator -> "wheels" Configurator
Configurator -> "windows" WindowDetails
WindowDetails -> "transparent" Configurator
WindowDetails -> "tinted" Configurator

but what this fails to capture is the argument to wheels (the number of wheels). and a regular grammar can't handle that because different integer arguments might lead to different classes (eg after "(2)" do you have a Configurator or a WindowDetails?):

Configurator -> "wheels" Integer
Configurator -> "windows" Integer
Integer -> ?

so it depends what you want. the chain of methods can be described by a regular grammar. but a regular grammar cannot also describe the arguments passed to the methods. afaict.

you can handle arguments by adding the complexity of context free grammars, because then you can do something like:

Configurator -> "wheels" Integer Configurator
Configurator -> "windows" Integer WindowDetails

which has the extra info needed to continue correctly after the integer argument.

NOTE: the above assumes that method names are unique across all classes. if you have two different classes with the same method name then you are going to have problems, obviously (i hope) (and this might not so rare if you are using things like "with" and "and"....)

andrew cooke
  • 45,717
  • 10
  • 93
  • 143
  • I'm fine with the arguments not being handled, they can't since we need to know compile time what class is being returned in order to present the next set of options. But what you're saying is that the class of grammars are **regular** grammars instead of **context free** right? – Dervall Mar 25 '12 at 19:18
  • the above uses regular grammars. i don't see any need for context free grammars in a simple method chain. they would be needed to handle the arguments (over simplifying, but anything with brackets tends to imply context free, because once the parentheses close you need to "pop back" to what you were doing before). so "yes". see update. – andrew cooke Mar 25 '12 at 19:21
  • I see, what I tried to do was to use a normal LR parsing table to generate "state" classes, but the problem is that you can't given a single state tell which state will be the target of a reduction. I could of course generate `ExpandoObject`s and keep track of the stack, but then I won't have syntax hightlighting, which is sort of the point. Seems that regular grammars are the way to go then – Dervall Mar 25 '12 at 19:40
  • i don't understand LR parsing, so i can't help you with the details. but since regular grammars are a subset of context free grammars i would have thought that you would be able to make it work. https://en.wikipedia.org/wiki/Chomsky_hierarchy - a regular grammar is really dumb. about all it can do is method chains. a cfg can do much more (*including* method chains). – andrew cooke Mar 25 '12 at 19:45