maetl / calyx

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

Breaking API changes for 0.9 release #11

Closed maetl closed 8 years ago

maetl commented 8 years ago

There’s a set of API changes I’d like to do for 0.8 as a precursor to locking down the final 1.0 API. They’ll be breaking changes unless I figure out a way to deprecate the existing API over multiple versions—still undecided on this.

Symmetric generate and evaluate methods

I’d like to remove the inconsistency mentioned in #8 by supporting a rule symbol to be passed as the first argument to generate and evaluate methods on the top level grammar. It should default to :start, but this is by convention, rather than a hard-coded part of the grammar structure.

#generate returns a flattened String of the evaluated template grammar:

# :start passed implicitly as the starting rule
grammar.generate

# :alternate_start passed explicitly as the starting rule
grammar.generate(:alternate_start)

#evaluate returns the connected nodes of a tree structure with the choice nodes flattened:

# :start passed implicitly as the starting rule
grammar.evaluate

# :alternate_start passed explicitly as the starting rule
grammar.evaluate(:alternate_start)

I’m prepared to drop the top level #evaluate feature if it requires too much wrangling and annoyance to implement, but I thought it might be quite interesting to return tree data structures—could potentially lead to some surprising and weird creative possibilities for generating references/markers/placeholders for additional processing steps—and would potentially allow this library to be used to create much more than just plain text strings.

Merging Multiple Grammars

Currently, passing a context map as the first argument to #generate actually constructs a new rule for each entry in the hash map, so ends up mutating the grammar object in-place, without seeming like it. I’d prefer to make this mutable behaviour explicit in the API so it’s more obvious.

grammar.merge!(rules_map)

grammar.combine(rules_map)

Alternatively, #generate and #evaluate will need to support both combinations of arguments (this is the context where ins and outs of possible breaking changes to the API need to be considered).

grammar.generate(:start_rule)
grammar.generate(:start_rule, rules_map)
grammar.generate(rules_map)
maetl commented 8 years ago

FYI @tra38 @mootpointer

tra38 commented 8 years ago

If I interpret it correctly, #generate returns the normal output, while #evaluate will return a series of tree nodes that another program can then use to generate something else? I could see some use in #evaluate, especially if attempting to replicate the Story Compiler approach used in MARY SUE. I would have to see how it works out though, and I don't have any plans on using #evaluate in the future (though if you do, then by all means implement it).

I think I prefer method-overloading for #generate, if only because I'm used to that API at the moment. I do not think that #generate actually creates new rules using a context map. What it does is duplicate the existing rules, then modify the duplicated rules in-place, and then use those rules to generate a text. Once the text is generated, the duplicated rules are thrown away, leaving the original rules untouched and ready to be used again. So if we want to implement a merge! or a combine method, we have to change the inner-workings of the code itself.

maetl commented 8 years ago

0.8.1 now supports multiple arguments passed to generate in either order, so that the change is backwards compatible with existing grammars.

All of the following should work:

grammar.generate
grammar.generate(:alt_start)
grammar.generate(:alt_start, { ... })
grammar.generate({ ... })
maetl commented 8 years ago

I’m going to close this for now, as 0.9 has landed without any significant breaking API changes.

However, I’m still considering the combine or merge style API for a future update. But for now, I think the core API can stay pretty much as-is.