comp.lang.ada
 help / color / mirror / Atom feed
From: Georg Bauhaus <rm-host.bauhaus@maps.futureapps.de>
Subject: Re: Parser interface design
Date: Fri, 08 Apr 2011 00:13:17 +0200
Date: 2011-04-08T00:13:18+02:00	[thread overview]
Message-ID: <4d9e36fd$0$6760$9b4e6d93@newsspool3.arcor-online.net> (raw)
In-Reply-To: <slrnips50m.2fnq.lithiumcat@sigil.instinctive.eu>


> I have trouble abstracting concept out of your examples, so if you don't
> mind I will provide my own, which I understand, and maybe we can work
> out a common ground that I understand from there.

Understood. (I know that example would be a bit involved. Too
much of it will confuse me, too, reliably.)


> Now at least I understand the idea of using generics instead:
>
>     generic
>        with function Emphasis (Contents: String) return String is<>;
>        with function Normal_Text (Contents: String) return String is<>;
>        with function Paragraph (Contents: String) return String is<>;
>     function Parser (Renderer: Renderer_Callbacks; Input: String)
>       return String;

I'll write about generics, first, because I think that even
if you decide against them (or their "spirit"), I think it is worth
knowing what they do. (There is no problem with functions not existing,
for example; Dmitry's war against generics is built on a different
fundament :-) Dmitry, I apologize!

The part between "generic" and "function" indeed corresponds to function
pointers, so that *is* the Renderer_Callbacks part, in Ada. Hence, the
parameter Renderer is not needed.

      generic
          with function Emphasis (Contents: String) return String is<>;
          ...
      function Parser (Input: String) return String;

The body of Parser will call Emphasis as needed (which is very much like
a function pointer)etc.

I'm assuming that the string returned is the rendered text.

In a C struct of function pointers, you'd make them point
to any function that fits the bill.
You do the same with generics.

    function HTML_Rendering_Parser is new Parser
       (Emphasis => EM_printing_function,
        Normal_Text => Identity,
        Paragraph => P_printing_function);

where EM_printing_function, Identity, and P_printing_function
are defined anywhere you like.  Or, in C99,

struct Parser HTMLRenderingParser = {
       .Emphasis = EM_printing_function,
       .Normal_Text = Identity,
       .Paragraph = P_Printing_function
    };

The difference is that in C (or equivalent Ada), the function
Parser is not a template to be instantiated and done, possibly
at compile time, but instead needs an object HTMLRenderingParser.
The similarity is that the functions are available to function
Parser in both cases.
In C, you pass an object that holds function pointers.
In Ada, using generics, you just pass function "addresses"
when instantiating,

In O-O only programming, you'd pass an object that corresponds
to a struct with pointers to functions and possibly a pointer
to another such struct (for inheritance of implementation).

Generic body, then:

      function Parser (Input: String) return String is
      begin
         loop
             ...
             subpart2 := Normal_Text("bar");
             part2 := Emphasis (subpart2);
             ...
         end loop;
      end Parser;

Where Emphasis stands for whatever function is passed
when instantiating Parser, etc.

But, away with generics, even though they will provide checking
and performance gains.  (No indirections through function pointers,
and guaranteed function existence! Instantiate with NOP functions
as needed.)



>> Anyway, here is another approach, probably not very original,
>> certainly lacking proper modularization and other things
>> that I did't see, sorry, but if offers another idea.  There aren't
>> any generics in it.
>>
>> But there is a procedure
>>
>>      procedure Print (Item : Token'Class;
>>                       Output : in out Format'Class);
>>
>> The idea is that given any token, Print renders the token
>> Item into any format passed for Output.
>
> I genuinely read several times your example, and I can't figure out what
> thing performs what.

OK.  There was one more actor.  I'll remove it in the next
step.  But first, assume that Tokens stand for atoms
or composites,


+--------+               +---------+
| Parser |  -> Token ->  | Printer | -> Rendered Token
+--------+               +---------+
                           |
                           v
              knows output format (polymorphic)

I understand you wanted the parser to be in control of the
printer, i.e., have Parser call Emphasis etc., instead.
I'll come to that.

Here is the parsing scheme I had in mind. The following is
simplified because it omits the composites (the semantic
units, which would be sort of handled via Printables, to be
extracted from "tokens").  The example establishes a different
kind of loose coupling. It shows Print at the center of
polymorphic behavior:

procedure Parsing is
     Output_Format: Format := Configuration.Choose;
begin
     loop
        declare
            X : Token'Class := Parser.Next_Token;
        begin
	   Print (X, Format);
        end;
     end loop;
end Parsing;

This control structure pulls tokens. Parser could be a "lazy"
object, producing tokens as needed.  (At this point, can you
see why I thought that, for example, the loop above could be
a task? Or the parser?  But that is a different story and of
no concern here.)

Then, next in the control structure, is a call on Print.
Print takes two arguments whose run-time type is not known.
By design, Print renders any kind of token into any format.
Print will therefore look at both X and Format, and find out what
they are (inspect their run-time tags).
Both X and Format could share O-O "interfaces", respectively.
Or X may be a variant record, the variants reflecting the different
kinds of token.

In the C style O-O you mentioned, Print would do that by
calling functions available through function pointers in
both X's struct and Format's struct.  Or, to learn about X,
Print could just look at a distinguishing field  of X.

Your idea seems different in that the parser will call the rendering
procedures whenever *it* thinks it has something ready to be rendered.
That's fine, I think.

Keeping your current design, just for the record,  you could establish
an interface in the logical sense,

    type Renderer_Callbacks is abstract tagged private;

    function Emphasis
       (This: Renderer_Callbacks; Contents: String) return String
    is abstract;

    function Normal_Text
       (This : Renderer_Callbacks; Contents: String) return String
    is abstract;

    function Paragraph
       (This : Renderer_Callbacks; Contents: String) return String
    is abstract;

Add derive types that will override behavior for
different output formats. Then

    function Parser
      (R : Renderer_Callbacks'Class; Input : String) return String;

The calls within Parser will be dispatching, depending on the
type of the actual renderer object.

And yes, if you don't start the hierarchy from reusable
functions that do not need to be overridden, there can be
code bloat.

This would not be the case with generics, which you
instantiate with whatever reusable functions you like!
:-)





  parent reply	other threads:[~2011-04-07 22:13 UTC|newest]

Thread overview: 37+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2011-04-06 10:11 Parser interface design Natasha Kerensikova
2011-04-06 12:17 ` Georg Bauhaus
2011-04-07 18:56   ` Natasha Kerensikova
2011-04-08 11:49     ` Stephen Leake
2011-04-06 12:20 ` Dmitry A. Kazakov
2011-04-07 19:14   ` Natasha Kerensikova
2011-04-07 20:31     ` Dmitry A. Kazakov
2011-04-08 13:51       ` Natasha Kerensikova
2011-04-08 14:21         ` Dmitry A. Kazakov
2011-04-12 15:58           ` Natasha Kerensikova
2011-04-12 17:14             ` Dmitry A. Kazakov
2011-04-06 15:51 ` Georg Bauhaus
2011-04-07 19:44   ` Natasha Kerensikova
2011-04-07 20:52     ` Dmitry A. Kazakov
2011-04-07 22:09     ` Simon Wright
2011-04-08 14:03       ` Natasha Kerensikova
2011-04-08 19:06         ` Jeffrey Carter
2011-04-08 19:59         ` Simon Wright
2011-04-12 16:13           ` Natasha Kerensikova
2011-04-12 17:22             ` Dmitry A. Kazakov
2011-04-12 19:02               ` Simon Wright
2011-04-13  8:20                 ` Natasha Kerensikova
2011-04-13  8:37                   ` Dmitry A. Kazakov
2011-04-13 11:06                     ` Georg Bauhaus
2011-04-13 12:46                       ` Dmitry A. Kazakov
2011-04-13 22:33                   ` Randy Brukardt
2011-04-14  6:55                     ` Natasha Kerensikova
2011-04-15  0:22                       ` Randy Brukardt
2011-04-12 21:54               ` Randy Brukardt
2011-04-07 22:13     ` Georg Bauhaus [this message]
2011-04-08 15:30       ` Natasha Kerensikova
2011-04-07  0:36 ` Randy Brukardt
2011-04-08 11:16 ` Brian Drummond
2011-04-19  9:08 ` Natasha Kerensikova
2011-04-19 12:35   ` Ludovic Brenta
2011-04-20 10:44     ` Brian Drummond
2011-04-19 17:28   ` Jeffrey Carter
replies disabled

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox