[squeak-dev] [ANN] Tirade

Göran Krampe goran at krampe.se
Wed Mar 18 14:46:26 UTC 2009


Hi!

(this thread is probably done now, but one last "devil in the 
details"-post for the insatiably interested...)

Nicolas Cellier wrote:
 > I wrote:
>> It might be worth noting that I want something simple that people can use
>> with very little effort. This means cutting some corners.
>>
>> But I also don't want to do "plain serialization", instead I want to create
>> a *custom* builder (serializers have a generic builder for all kinds of
>> Smalltalk objects) that works in *tandem* with the domain objects it
>> constructs (by utilizing real instance creation methods taking arguments and
>> not just "basicNew" followed by stuffing ivars) and that can be driven by
>> Tirade messages.
>>
>> The above paragraph catches it quite well I think. So construction looks
>> like this perhaps:
> 
> Agree.
> My "serialization" was customized to use higher level messages to re-build
> the objects (a public API)...
> ...rather than lower level inst var description.
> When necessary, with help of a builder (stored in a predefined file scope
> variable).

Ok, so in fact a bit similar in philosophy.

>> "tirade message sequence" => TiradeParser (parses) => TiradeStackReader
>> (security checks and maintaining stack of receivers) => DeltaBuilder =>
>> "delta domain objects"
>>
>> Now, a very small example:
>>
>> Tirade input:
>> ---------------
>> createDelta: 'Name of delta'.
>>  addRenameClass: #OldClassName.
>>    newClassName: #NewClassName.
>>  end.
>>  addRenameClass: #AnotherOldClassName.
>>    newClassName: #AnotherNewClassName.
>>  end.
>> end.
>> -----------------------------
>>
>> DeltaBuilder>>createDelta: aName
>>        ^DSDelta named: aName
>>
>> DSDelta>>addRenameClass: oldClassName
>>     "Here we return the DSClassRenameChange instance.
>>     Thus it will be the stacked receiver for Tirade messages."
>>     ^self addChange: (DSClassRenameChange from: oldClassName)
>>
>> DSClassRenameChange>>newClassName: newClassName
>>     newName := newClassName
>>
>>
>> ...ok, so the DeltaBuilder creates a Delta and returns it as the next
>> receiver on the stack.
>>
>> Then comes another message to add a "rename class" change. (A Delta is a
>> sequence of Changes basically). We do that BUT we also return this new
>> DSClassRenameChange object so that it will be the next receiver.
>>
>> #newClassName: is thus not sent to the DSClassRenameChange object, setting
>> one of its attributes. It returns self so it will still be the receiver for
>> more messages. If it returned nil it would cause the TiradeStackReader to
>> pop it, thus an object can actually "pop itself".
>>
>> More likely the Tirade input knows when we are done setting attributes so
>> it sends #end. This message is a message that TiradeStackReader intercepts
>> and causes it to pop the stack. The final #end pops the Delta too.
>>
>> Note that we are not naming ivars in the Tirade input, we aren't even
>> naming the class DSDelta! Everything is a message with data as arguments.
>>
>> regards, Göran
>
> OK, this is cute, both syntax and implementation are simple, efficient and
> readable.

Thanks! Yeah, I like it so far, although the stack bits aren't set in 
stone yet - see below.

> I was fearing this would apply only to a collection of flat objects.
> ... and any deeper level would require another strategy, like using a JSON
> like literal array...

No, definitely not - in that case it would be rather ... dull. :)

> ... unless, each object acts as the builder for its next level in hierarchy.
> Here DSDelta acts as the builder for building a DSClassRenameChange.
> This is certainly a good pattern.

Yes, but I don't want to "hardcode" this pattern into Tirade, which is 
why I separate some things from the TiradeParser (language) into 
TiradeReader (subclass of parser that actually "does" something) and 
also into the builder object(s).

My current view on the responsibilities:

TiradeParser: Parses an input stream of Tirade messages. Completely 
defines the syntax of Tirade. If you use TiradeParser on a stream you 
will get printouts in Transcript because it doesn't really *do* anything 
with all the messages it reads. TiradeParser knows nothing about 
receivers, it just parses an endless sequence of messages.

The reader: A reader is a subclass of TiradeParser and implements 
#processMessage. It could do whatever it likes, printing them, 
constructing an object - beep, whatever. :)


TiradeReader: The only included reader class in the Tirade package. It 
deals with three things:

	1. Figuring out the receiver for the message.
	2. Making security checks on the message to be sent.
	3. How to actually send the message.

Figuring out the receiver is done by using a stack of receivers and 
following some conventions around that stack.

The security check right now is based on checking the method category 
and verifying it beginsWith: 'tirade'. Otherwise there is an error. If 
there is no implementation (then the receiver probably has implemented 
doesNotUnderstand:) there is no check, we presume it does its own 
checking. There is also a "whitelist" of allowed messages that is empty 
by default. Using this you can allow receivers like blocks etc (allowing 
#value:) or such.

How to send the message. This is interesting, I added a Set of 
"controlMessages" in the reader in which the builder can register 
messages that *it* wants to receive and not the current receiver on the 
stack. This makes it possibly for the builder to pre-register for 
example stack manipulation messages and when it receives them it can 
manipulate the reader "from the outside" regardless of the receiver stack.

This is very nice because then the builder is "in full control" and can 
decide how to deal with Tirade input. Even if the input stream says 
"end." the builder can decide to not pop the current receiver.

Anyway, I find Tirade to be quite nice so far, and encourage anyone 
interested in "file formats" or similar to give feedback. The SS repo is 
open for writes :)

regards, Göran




More information about the Squeak-dev mailing list