maetl / calyx

A Ruby library for generating text with recursive template grammars.
MIT License
62 stars 5 forks source link

Start method is technically unnecessary #8

Closed tra38 closed 8 years ago

tra38 commented 8 years ago
class SelfAware < Calyx::Grammar
   rule :start, "I am self-aware."
end

robot = SelfAware.new
expect(robot.generate).to eq("I am self-aware.")

I discovered this "feature" while implementing YAML/JSON support, when I had a test that was supposed to fail wind up passing anyway. I suspect that this is because "start" is the first rule that is called when you call Calyx::Grammar#generate, so it doesn't matter if you define the start rule using the 'start' method or the long-hand 'rule' method. As long as the start rule is defined in some fashion, Calyx will find it.

I'm not sure if this should even be counted as a bug or not. On one hand, it's not causing any problems. On the other hand, this 'undocumented feature' could break in the future. Either way, I might as well write this issue to document that this oddity that exists within our codebase.

maetl commented 8 years ago

The test described above should never fail, in keeping with the basic expectation of consistency when declaring rules in Ruby. For the time being, both ways of defining the start rule should work. This ‘oddity’ will disappear if a method_missing DSL for rules is implemented in Ruby—and it’s not an issue for grammars defined in text formats like YAML or JSON, as there’s no concept of ‘methods’ or a DSL in those formats.

The start rule is just an arbitrary convention/shortcut, mostly there for documentation/understanding how to get started. I recently did a small refactoring to the internal method signatures to make this more explicit: 492e7d8

There’s no particular reason why this has to be hard-coded, and in the long term, it might be better to allow users to pass in any symbol they want to use as a starting rule, rather than forcing each grammar to have an explicit :start symbol. This would add more flexibility to grammars, as it would enable larger grammars to support many different use cases, rather than having to break these grammars up into separate classes as the current API demands.

grammar.generate(:any_symbol)

I would be happy to remove the arbitrary start method at some stage (replacing it with a consistent DSL layer with a pure convention based :start symbol as the default first argument to generate).