caml-list - the Caml user's mailing list
 help / color / mirror / Atom feed
* menhir
@ 2007-04-28 10:32 skaller
  2007-04-28 16:50 ` [Caml-list] menhir Francois Pottier
  0 siblings, 1 reply; 20+ messages in thread
From: skaller @ 2007-04-28 10:32 UTC (permalink / raw)
  To: caml-list

Just a note I just built the Felix parser with Menhir.
First, it detected some duplicate definitions Ocamlyacc didn't! Good!

Second, I got a "rather a lot" of states have end-of-stream conflicts.
What's that about?

Third the generated ml file was 4.5 Meg. 
Ocamlopt on amd64 hung for so long I almost posted a bug report
for Ocamlopt, but finally it finished. This was a 95% CPU, 25% memory
job, so no paging. I'd guess it took 100x times longer than
compilation of the ocamlyacc file (which is just a bunch of numbers :)
I didn't measure it .. no biggie for me now I know, but my
box is a LOT faster than some of the boxes my product gets built on.

After that, Felix built ok, and the parser worked for
'pure' code. However it failed when Felix preprocessor
syntax extensions were used (which is 90% of all programs).

Now, most of the system data transport for this is properly
built so it can't cause any problems. The one thing which 
is hacked is the pushback detection.

Basically: when Ocamlyacc reduces a production, it sometimes
ends on the last token, and sometimes it overshoots by 1.

My grammar uses a system like:

exprx:
  expr expr_terminator { $1,$2 }

statementsx:
  | statement_aster statements_terminator { $1, $2 }

This is saying: a 'special expression' exprx is an expression
PLUS one of the tokens which will solidly terminate an 
expression AND NOT ITSELF BE OVERSHOT by the parser.

In other words when exprx is parsed the reduction must
leave the next token unread.

The semantics used are: when the exprx is processed the
action arranges to push the terminator token back
into the token stream.

Perhaps because Menhir is LR(1) not LALR(1), this technique
is failing. Or Menhir may simply be looking ahead further
than required in the token stream.

Whichever way, I am depending on this particular implementation
detail of Ocamlyacc, and Mehir is using a different implementation.

Any suggestions how to 'fix' this?

-- 
John Skaller <skaller at users dot sf dot net>
Felix, successor to C++: http://felix.sf.net


^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [Caml-list] menhir
  2007-04-28 10:32 menhir skaller
@ 2007-04-28 16:50 ` Francois Pottier
  2007-04-28 19:47   ` Markus Mottl
  2007-04-29  4:43   ` skaller
  0 siblings, 2 replies; 20+ messages in thread
From: Francois Pottier @ 2007-04-28 16:50 UTC (permalink / raw)
  To: skaller; +Cc: caml-list


Hello,

On Sat, Apr 28, 2007 at 08:32:16PM +1000, skaller wrote:
> Second, I got a "rather a lot" of states have end-of-stream conflicts.
> What's that about?

That's precisely about "overshooting" the end of the token stream (reading
one too many tokens). The issue is described in the manual.

> Third the generated ml file was 4.5 Meg.  Ocamlopt on amd64 hung for so long
> I almost posted a bug report for Ocamlopt, but finally it finished.

Yup, I am somewhat disappointed that ocamlopt does not seem to have linear
time complexity, but I shouldn't complain too loud, my boss may be listening
:)

An option to generate tables like ocamlyacc would clearly be useful.

> Basically: when Ocamlyacc reduces a production, it sometimes
> ends on the last token, and sometimes it overshoots by 1.

I don't know the details of your grammar, but our (perhaps naive) view is that
you should modify your grammar to avoid end-of-stream conflicts (and Menhir's
conflict reports will help you do that). Then, Menhir will not overshoot.

> The semantics used are: when the exprx is processed the
> action arranges to push the terminator token back
> into the token stream.

How is this done? It is possible that this hack is not compatible with Menhir,
because Menhir keeps a local cache of the next token, which is not properly
updated when you push your old token back.

-- 
François Pottier
Francois.Pottier@inria.fr
http://cristal.inria.fr/~fpottier/


^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [Caml-list] menhir
  2007-04-28 16:50 ` [Caml-list] menhir Francois Pottier
@ 2007-04-28 19:47   ` Markus Mottl
  2007-04-28 21:15     ` Jon Harrop
  2007-04-29  4:43   ` skaller
  1 sibling, 1 reply; 20+ messages in thread
From: Markus Mottl @ 2007-04-28 19:47 UTC (permalink / raw)
  To: Francois.Pottier; +Cc: skaller, caml-list

On 4/28/07, Francois Pottier <Francois.Pottier@inria.fr> wrote:
> Yup, I am somewhat disappointed that ocamlopt does not seem to have linear
> time complexity, but I shouldn't complain too loud, my boss may be listening
> :)

This is unfortunate indeed.  I have also run into problems with the
compilers exhibiting quadratic behavior on very large sources.  It
seems that this mostly stems from the use of data structures in the
compiler that scale badly on certain operations (e.g. searching
through lists rather than in balanced trees).

I think it wouldn't be difficult to update the compiler to use more
efficient data structures, but it would certainly be a hell lot of
work...

Regards,
Markus

-- 
Markus Mottl        http://www.ocaml.info        markus.mottl@gmail.com


^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [Caml-list] menhir
  2007-04-28 19:47   ` Markus Mottl
@ 2007-04-28 21:15     ` Jon Harrop
  0 siblings, 0 replies; 20+ messages in thread
From: Jon Harrop @ 2007-04-28 21:15 UTC (permalink / raw)
  To: caml-list

On Saturday 28 April 2007 20:47, Markus Mottl wrote:
> I think it wouldn't be difficult to update the compiler to use more
> efficient data structures, but it would certainly be a hell lot of
> work...

I've had a quick look at this before. Some of our compiler compilers were 
causing ocamlopt to stack overflow and the problem turned out to be a pair of 
mutually recursive functions in different modules of the type checkers that 
were not tail recursive. I couldn't see an easy solution without rewriting 
the whole thing in CPS.

Perhaps a program to convert OCaml code into CPS style would be useful?

-- 
Dr Jon D Harrop, Flying Frog Consultancy Ltd.
The F#.NET Journal
http://www.ffconsultancy.com/products/fsharp_journal/?e


^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [Caml-list] menhir
  2007-04-28 16:50 ` [Caml-list] menhir Francois Pottier
  2007-04-28 19:47   ` Markus Mottl
@ 2007-04-29  4:43   ` skaller
  2007-04-29  7:27     ` Christophe Raffalli
  2007-05-01 15:57     ` Francois Pottier
  1 sibling, 2 replies; 20+ messages in thread
From: skaller @ 2007-04-29  4:43 UTC (permalink / raw)
  To: Francois.Pottier; +Cc: caml-list

On Sat, 2007-04-28 at 18:50 +0200, Francois Pottier wrote:

> > Third the generated ml file was 4.5 Meg.  Ocamlopt on amd64 hung for so long
> > I almost posted a bug report for Ocamlopt, but finally it finished.
> 
> Yup, I am somewhat disappointed that ocamlopt does not seem to have linear
> time complexity, but I shouldn't complain too loud, my boss may be listening
> :)
> 
> An option to generate tables like ocamlyacc would clearly be useful.

Although that defeats one of the future advantages of Menhir:
embedding. To understand what I mean you might look at the Felix
parser generator. In Felix you can write parsers in any scope,
and the user actions bind to the containing scope.

This means you can think about mixing programmatic recursive 
descent with the parser automaton. An executable recursive
descent parser is easily extended dynamically (but has woeful
error handling ;(

For Menhir, embedding would be nice. However even without
it being able to use recursion and actually pass arguments --
which ocamlyacc does not support -- should allow decoupling
of some of your grammar into smaller subgrammars, and this
in turn could reduce compile time.

It is possible (but I'm no expert!!) that the non-linearity
in compiling Menhir generated code is due to a very 
large let rec/and/... being generated. Some extra analysis 
might fix that, i.e. using recursion only when necessary,
and let/and/in otherwise (though I have no idea how to
do that).

For embedding the 'one token overshoot' is a bit nasty.

OTOH with GLR parser, extra functionality is gained,
and user actions must have no side effects, so
the idea to 'push back' the overshoot token cannot be used
(since it is a side-effect), at least without some thought.. 
hmmm ..

An extensible GLR+ parser would be interesting. This is 
an extensible list of GLR parsers that run in 'parallel',
using the GLR idea, but the list being dynamically extensible.
Exactly how you'd manage it i don't know.

The theoretical underpinnings should be the same as
for open-recursion idea: make a grammar with a parameter
representing extra productions, then you can fix it
to close the grammar. This should at least be possible
statically (at compile time).


> > Basically: when Ocamlyacc reduces a production, it sometimes
> > ends on the last token, and sometimes it overshoots by 1.
> 
> I don't know the details of your grammar, but our (perhaps naive) view is that
> you should modify your grammar to avoid end-of-stream conflicts (and Menhir's
> conflict reports will help you do that). Then, Menhir will not overshoot.

Actually in my grammar there is a definite user defined ENDMARKER
for the main grammar, and 'end-of-expression' and 'end-of-statements'
set of tokens for the extensible parts. The input is not a file,
it's a list of tokens built by other means.

So the conflicts are spurious: end of stream can't be parsed
(but of course Menhir doesn't know that). This is good,
because Mehir CANNOT detect end of stream, since my lexbuf
is a dummy and is not used at all.


> > The semantics used are: when the exprx is processed the
> > action arranges to push the terminator token back
> > into the token stream.
> 
> How is this done? 

The lexer function is bound to a class object which contains
a mutable list of tokens. The actual lexbuf is ignored,
its a dummy. So pushing token onto the list is just

	ts <- last_token :: ts

:)


> It is possible that this hack is not compatible with Menhir,
> because Menhir keeps a local cache of the next token, which is not properly
> updated when you push your old token back.

That's what I would have guessed.

In order to use an LR parser recursively, which I do, it must
be possible to control the lookahead token somehow. My code
just uses an implementation detail of Ocamlyacc discovered
by accident, and the detail differs for Menhir.


-- 
John Skaller <skaller at users dot sf dot net>
Felix, successor to C++: http://felix.sf.net


^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [Caml-list] menhir
  2007-04-29  4:43   ` skaller
@ 2007-04-29  7:27     ` Christophe Raffalli
  2007-05-01 15:57     ` Francois Pottier
  1 sibling, 0 replies; 20+ messages in thread
From: Christophe Raffalli @ 2007-04-29  7:27 UTC (permalink / raw)
  To: caml-list


Dear list members,
>
> OTOH with GLR parser, extra functionality is gained,
> and user actions must have no side effects, so
> the idea to 'push back' the overshoot token cannot be used
> (since it is a side-effect), at least without some thought.. 
> hmmm ..
>   
dypgen uses GLR and allows side effect, because it keeps a state
associated to each stack.
Actually two states, one global on one "top down": typically the top
down state can be used to carry
information about bound variables down in the parse tree.
> An extensible GLR+ parser would be interesting. This is 
> an extensible list of GLR parsers that run in 'parallel',
> using the GLR idea, but the list being dynamically extensible.
> Exactly how you'd manage it i don't know.
>   
dypgen allows the action to extend the grammar (in a precise scope)

About table/code size, dypgen can use (the user choose) LR0, LALR or LR1
automaton
to drive the parser. Moreover, priorities can be handled (this is the
default) outside the automaton, giving
very small tables.

Christophe Raffalli



^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [Caml-list] menhir
  2007-04-29  4:43   ` skaller
  2007-04-29  7:27     ` Christophe Raffalli
@ 2007-05-01 15:57     ` Francois Pottier
  2007-05-01 17:11       ` skaller
  2007-05-01 17:15       ` skaller
  1 sibling, 2 replies; 20+ messages in thread
From: Francois Pottier @ 2007-05-01 15:57 UTC (permalink / raw)
  To: skaller; +Cc: caml-list


Hello,

On Sun, Apr 29, 2007 at 02:43:03PM +1000, skaller wrote:
> > An option to generate tables like ocamlyacc would clearly be useful.
> 
> Although that defeats one of the future advantages of Menhir:
> embedding. To understand what I mean you might look at the Felix
> parser generator. In Felix you can write parsers in any scope,
> and the user actions bind to the containing scope.

I don't see why there is a relationship between the style of code
generation (code versus tables) and embedding.

Embedding in Menhir is supported indirectly via %parameter directives.

> It is possible (but I'm no expert!!) that the non-linearity
> in compiling Menhir generated code is due to a very 
> large let rec/and/... being generated. Some extra analysis 
> might fix that, i.e. using recursion only when necessary,
> and let/and/in otherwise (though I have no idea how to
> do that).

I have tried that, but it does not help. Most (say, 95%) of the functions
generated by Menhir are truly mutually recursive.

> So the conflicts are spurious: end of stream can't be parsed
> (but of course Menhir doesn't know that). This is good,
> because Mehir CANNOT detect end of stream, since my lexbuf
> is a dummy and is not used at all.

Perhaps the terminology "end-of-stream conflict" is badly chosen. These
conflicts are not just about detecting the end of the stream: they are about
recognizing a non-terminal symbol without overshooting, that is, without
unnecessarily requesting a lookahead token (a request which, if the end of
stream has been reached, could be unsatisfiable, or could be blocking).

These conflicts are not "spurious": just like shift/reduce or reduce/reduce
conflicts, they will cause trouble.

-- 
François Pottier
Francois.Pottier@inria.fr
http://cristal.inria.fr/~fpottier/


^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [Caml-list] menhir
  2007-05-01 15:57     ` Francois Pottier
@ 2007-05-01 17:11       ` skaller
  2007-05-01 17:34         ` Francois Pottier
  2007-05-01 17:15       ` skaller
  1 sibling, 1 reply; 20+ messages in thread
From: skaller @ 2007-05-01 17:11 UTC (permalink / raw)
  To: Francois.Pottier; +Cc: caml-list

On Tue, 2007-05-01 at 17:57 +0200, Francois Pottier wrote:

> > So the conflicts are spurious: end of stream can't be parsed
> > (but of course Menhir doesn't know that). This is good,
> > because Mehir CANNOT detect end of stream, since my lexbuf
> > is a dummy and is not used at all.
> 
> Perhaps the terminology "end-of-stream conflict" is badly chosen. These
> conflicts are not just about detecting the end of the stream: they are about
> recognizing a non-terminal symbol without overshooting, that is, without
> unnecessarily requesting a lookahead token (a request which, if the end of
> stream has been reached, could be unsatisfiable, or could be blocking).
> 
> These conflicts are not "spurious": just like shift/reduce or reduce/reduce
> conflicts, they will cause trouble.

They're not technically spurious, but for my grammar they're
spurious because the input language isn't arbitrary, it
always has an ENDMARKER at the end of the token stream,
and the parser never processes any symbols past that.

It can't hit 'end of stream' because that's a state
of the lexbuf .. and the lexbuf is is a dummy which is
never updated or modifed -- so it can't ever be in
the end-of-stream state.

So basically, menhir is saying "what happens if you hit
end of stream here? What should be done?" and the answer
is "it can't happen".

BTW: Ocamlyacc doesn't report any conflicts, so this
is a minor incompatibility.


-- 
John Skaller <skaller at users dot sf dot net>
Felix, successor to C++: http://felix.sf.net


^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [Caml-list] menhir
  2007-05-01 15:57     ` Francois Pottier
  2007-05-01 17:11       ` skaller
@ 2007-05-01 17:15       ` skaller
  2007-05-01 17:31         ` Francois Pottier
  1 sibling, 1 reply; 20+ messages in thread
From: skaller @ 2007-05-01 17:15 UTC (permalink / raw)
  To: Francois.Pottier; +Cc: caml-list

On Tue, 2007-05-01 at 17:57 +0200, Francois Pottier wrote:

> I have tried that, but it does not help. Most (say, 95%) of the functions
> generated by Menhir are truly mutually recursive.

Yes, but you can always use indirection to decouple them
can't you? Eg just use a 'ref' to a function?

Slower at run time, but should fix the compile time
problem if the cause is huge let/recs.

-- 
John Skaller <skaller at users dot sf dot net>
Felix, successor to C++: http://felix.sf.net


^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [Caml-list] menhir
  2007-05-01 17:15       ` skaller
@ 2007-05-01 17:31         ` Francois Pottier
  0 siblings, 0 replies; 20+ messages in thread
From: Francois Pottier @ 2007-05-01 17:31 UTC (permalink / raw)
  To: skaller; +Cc: caml-list


> Yes, but you can always use indirection to decouple them
> can't you? Eg just use a 'ref' to a function?

I would need a ref to a huge record of functions, or a lot of individual refs.
I have tried some of the possibilities, but they yield ugly code and none was
actually faster.

-- 
François Pottier
Francois.Pottier@inria.fr
http://cristal.inria.fr/~fpottier/


^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [Caml-list] menhir
  2007-05-01 17:11       ` skaller
@ 2007-05-01 17:34         ` Francois Pottier
  2007-05-01 23:42           ` skaller
  0 siblings, 1 reply; 20+ messages in thread
From: Francois Pottier @ 2007-05-01 17:34 UTC (permalink / raw)
  To: skaller; +Cc: caml-list


> So basically, menhir is saying "what happens if you hit
> end of stream here? What should be done?" and the answer
> is "it can't happen".

What I am trying to say is, Menhir doesn't ask this question. It asks:
"what happens if I reach that state? should I request a lookahead token
from the lexer, or shouldn't I?"

-- 
François Pottier
Francois.Pottier@inria.fr
http://cristal.inria.fr/~fpottier/


^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [Caml-list] menhir
  2007-05-01 17:34         ` Francois Pottier
@ 2007-05-01 23:42           ` skaller
  2007-05-02  5:38             ` Francois Pottier
  0 siblings, 1 reply; 20+ messages in thread
From: skaller @ 2007-05-01 23:42 UTC (permalink / raw)
  To: Francois.Pottier; +Cc: caml-list

On Tue, 2007-05-01 at 19:34 +0200, Francois Pottier wrote:
> > So basically, menhir is saying "what happens if you hit
> > end of stream here? What should be done?" and the answer
> > is "it can't happen".
> 
> What I am trying to say is, Menhir doesn't ask this question. It asks:
> "what happens if I reach that state? should I request a lookahead token
> from the lexer, or shouldn't I?"

Perhaps I do not fully understand but:

I do not use 'eof' (end-of-stream pseudo token).
I get this for an unused token:

"Warning: the token WHENCE is unused."

and I should get the same for eof IMHO (but possibly 
suppressed message since it's not a user defined
token).

What i actually get is 145 end-of-stream conflicts .. 
but i get no conflicts on WHENCE.

That is, instead of treating the token set precisely as the
set of user tokens + eof, menhir is treating eof specially
and not consistently with other tokens.

There's no conflict: in every one of these states if 'eof'
turns up its a syntax error, exactly the same as if WHENCE
turns up. 

I'm not sure i fully understand the 'do we need lookahead'
issue: I would have thought: you need a fetch if your action is 
shift, and not if it is a reduce.



-- 
John Skaller <skaller at users dot sf dot net>
Felix, successor to C++: http://felix.sf.net


^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [Caml-list] menhir
  2007-05-01 23:42           ` skaller
@ 2007-05-02  5:38             ` Francois Pottier
  2007-05-02  5:50               ` Francois Pottier
  2007-05-02  8:41               ` skaller
  0 siblings, 2 replies; 20+ messages in thread
From: Francois Pottier @ 2007-05-02  5:38 UTC (permalink / raw)
  To: skaller; +Cc: caml-list


Hello,

> That is, instead of treating the token set precisely as the
> set of user tokens + eof, menhir is treating eof specially
> and not consistently with other tokens.

As far as Menhir is concerned, there is no special eof token.
The set of tokens is exactly the set of user-defined tokens.
This is true also of ocamlyacc.

Internally, the construction of the automaton uses a pseudo-token,
written #, which stands for the end of the token stream. This token
can appear in conflict explanation messages.

> I'm not sure i fully understand the 'do we need lookahead'
> issue: I would have thought: you need a fetch if your action is 
> shift, and not if it is a reduce.

What you mean is, you need to *consume* one token if your action
is shift, and none if your action is reduce. However, in order to
make a decision (in order to choose between shift and reduce, and
between multiple reduce actions), you sometimes need to *consult*
one lookahead token, without consuming it.

This is necessary only *sometimes*, because, in some states, only
one reduce action is possible, and can be taken without consulting
a lookahead token.

An end-of-stream conflict arises when a state has multiple
actions, some of which are on real tokens, and some of which are
on the pseudo-token #. In that case, one would like to consult
the next token in order to make a decision; however, there is a
possibility that we are at the end of the sentence that we are
trying to recognize, so asking the lexer for one more token might
be a mistake (in your terminology, it might be an overshoot).

Does this clarify things?

-- 
François Pottier
Francois.Pottier@inria.fr
http://cristal.inria.fr/~fpottier/


^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [Caml-list] menhir
  2007-05-02  5:38             ` Francois Pottier
@ 2007-05-02  5:50               ` Francois Pottier
  2007-05-02  8:41               ` skaller
  1 sibling, 0 replies; 20+ messages in thread
From: Francois Pottier @ 2007-05-02  5:50 UTC (permalink / raw)
  To: skaller; +Cc: caml-list


On Wed, May 02, 2007 at 07:38:15AM +0200, I wrote:
> Internally, the construction of the automaton uses a pseudo-token,
> written #, which stands for the end of the token stream. This token
> can appear in conflict explanation messages.

Actually, I should say: # stands for the end of a sentence that we are trying
to recognize. That is, if S is a start symbol, then Menhir builds a new start
symbol S' with the production S' -> S #. As a result, when we reach a state
where a reduce action ("reduce production p") has lookahead symbol #, this
means: "in this state, perhaps we have consumed a sentence derived from S; in
that case, we should reduce production p and not read anything more". It does
not mean that we have reached the end of the physical token stream, only that
we have reached the end of what we were supposed to read.

-- 
François Pottier
Francois.Pottier@inria.fr
http://cristal.inria.fr/~fpottier/


^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [Caml-list] menhir
  2007-05-02  5:38             ` Francois Pottier
  2007-05-02  5:50               ` Francois Pottier
@ 2007-05-02  8:41               ` skaller
  2007-05-02 12:30                 ` Francois Pottier
  1 sibling, 1 reply; 20+ messages in thread
From: skaller @ 2007-05-02  8:41 UTC (permalink / raw)
  To: Francois.Pottier; +Cc: caml-list

On Wed, 2007-05-02 at 07:38 +0200, Francois Pottier wrote:
> Hello,
> 
> > That is, instead of treating the token set precisely as the
> > set of user tokens + eof, menhir is treating eof specially
> > and not consistently with other tokens.
> 
> As far as Menhir is concerned, there is no special eof token.
> The set of tokens is exactly the set of user-defined tokens.
> This is true also of ocamlyacc.
> 
> Internally, the construction of the automaton uses a pseudo-token,
> written #, which stands for the end of the token stream. This token
> can appear in conflict explanation messages.

Exactly. In Ocamlyacc it is named 'eof', and you can use that
token in your productions.

> > I'm not sure i fully understand the 'do we need lookahead'
> > issue: I would have thought: you need a fetch if your action is 
> > shift, and not if it is a reduce.
> 
> What you mean is, you need to *consume* one token if your action
> is shift, and none if your action is reduce. However, in order to
> make a decision (in order to choose between shift and reduce, and
> between multiple reduce actions), you sometimes need to *consult*
> one lookahead token, without consuming it.
> 
> This is necessary only *sometimes*, because, in some states, only
> one reduce action is possible, and can be taken without consulting
> a lookahead token.

Right.

> An end-of-stream conflict arises when a state has multiple
> actions, some of which are on real tokens, and some of which are
> on the pseudo-token #. In that case, one would like to consult
> the next token in order to make a decision; however, there is a
> possibility that we are at the end of the sentence that we are
> trying to recognize, so asking the lexer for one more token might
> be a mistake (in your terminology, it might be an overshoot).
> 
> Does this clarify things?

Yes. Your code or my grammar is bugged :)

My grammar is 

compilation_unit:
  | statement_aster ENDMARKER { $1 }

where no non-terminal on which statements_aster depends 
has a production containing ENDMARKER or # (eof).

Therefore, there is no conflict. When compilation_unit
is reduced the parser returns, the next token, whether
it is # or any other, is irrelevant.

The parser is therefore required to match the head
of the input stream and NOT the whole stream.

It seems Menhir wants to match the whole input,
and is confused because it doesn't know what to
do after it sees ENDMARKER.

That means it doesn't detect that compilation_unit
is a start symbol .. and the reduction is final.
If you are augmenting the grammar to be:

compilation_unit:
	| statement_aster ENDMARKER eof { $1 }

that would explain Menhir's confusion .. however the
generated parser actually does work (provided the
user defined syntax feature isn't used).

IMHO: the end-of-stream thing is a serious bug in the
design of Ocamlyacc which unfortunately Menhir duplicates.

In particular there should be no possibility of the parser
consulting the lexbuf, the signature of parsers is very
wrong:

	parser: (lexbuf -> token) -> lexbuf -> ast

is a disaster. Because of this you're duplicating the
disaster and probing the lexbuf for end of stream,
which is just wrong. 

There should be no implicit end of stream: a stream
is an INFINITE sequence, a parser should process the
head of the stream, and the signature should be:

	parser: (state -> token) -> state -> ast

where state is a USER defined type. If the user provides
no state argument, then it defaults to unit.

This denies the parser any access to a hidden 'end of stream'
token. It's up to the user to decide what terminates the
parse. The point again is that the token input to the
parser is infinite: it can't ever be an error to read 
a next token.

The problem is the usual one for streams. Probably the
interface should be changed to something like:

	peek state n (* look ahead n symbols *)
	read state   (* destructive read 1 symbol *)
	loc state    (* return location for diagnostics *)

This would support LR(infinity) parsing.

A better interface would support recursive descent too,
in that case you'd have to make the 'streaming' 
persistent to support backtracking, that is you'd have to use:

	state -> (token * state)

as the lexer interface -- that gets rid of the push back problem.
(This is trivial to implement on top of a destructive stream,
by buffering with lists: Extlib streams do just that).

Dypgen actually does a clever trick here, since GLR
also requires actions which are purely functional ..
from the outside (i.e. persistent): it allow mutations
and they propagate locally in the current branch
(L-attributes yes?). It also allows global mutations,
but they only become permanent when all the branches
agree on them.

Anyhow the problem seems to reduce to this: I think
a parser should read the head of a stream, and Menhir
thinks it should read the whole stream, and it uses
the bugged lexbuf interface to test for end of stream.

So I could fix my grammar by adding 'end-of-stream'
to my start symbols AND hack the otherwise unused
lexbuf to show end-of-stream after ENDMARKER is read.

Ocamlyacc doesn't complain though, suggesting it processes
only the head of a stream .. and that there's a fundamental
design distinction between the two parser generators.



-- 
John Skaller <skaller at users dot sf dot net>
Felix, successor to C++: http://felix.sf.net


^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [Caml-list] menhir
  2007-05-02  8:41               ` skaller
@ 2007-05-02 12:30                 ` Francois Pottier
  2007-05-02 16:29                   ` skaller
  0 siblings, 1 reply; 20+ messages in thread
From: Francois Pottier @ 2007-05-02 12:30 UTC (permalink / raw)
  To: skaller; +Cc: caml-list

[-- Attachment #1: Type: text/plain, Size: 4788 bytes --]


Hello,

On Wed, May 02, 2007 at 06:41:44PM +1000, skaller wrote:
> Exactly. In Ocamlyacc it is named 'eof', and you can use that
> token in your productions.

As far as I know, this is incorrect. ocamlyacc does not have a predefined
eof token. Perhaps you are thinking of ocamllex, which has an eof pattern.

> compilation_unit:
>   | statement_aster ENDMARKER { $1 }
> 
> where no non-terminal on which statements_aster depends 
> has a production containing ENDMARKER or # (eof).
> 
> Therefore, there is no conflict. When compilation_unit
> is reduced the parser returns, the next token, whether
> it is # or any other, is irrelevant.

Good. I seem to agree with you. Menhir should not report an end-of-stream
conflict here. So, what does it report?

> If you are augmenting the grammar to be:
> 
> compilation_unit:
> 	| statement_aster ENDMARKER eof { $1 }

No, actually, I am augmenting the grammar with the production
compilation_unit' -> compilation_unit, and using
compilation_unit' -> compilation_unit { # } as my initial LR(1)
item in the construction of the automaton.

> IMHO: the end-of-stream thing is a serious bug in the
> design of Ocamlyacc which unfortunately Menhir duplicates.

I believe ocamlyacc's behavior is reasonable but subtle and undocumented...
Menhir attempts to reproduce that behavior, while providing more guidance in
the form of end-of-stream conflict reports... That said, I could be missing a
point. What is the bug?

> In particular there should be no possibility of the parser
> consulting the lexbuf, the signature of parsers is very
> wrong:
> 
> 	parser: (lexbuf -> token) -> lexbuf -> ast
> 
> is a disaster. Because of this you're duplicating the
> disaster and probing the lexbuf for end of stream,
> which is just wrong. 
> 
> There should be no implicit end of stream: a stream
> is an INFINITE sequence, a parser should process the
> head of the stream, and the signature should be:
> 
> 	parser: (state -> token) -> state -> ast
> 
> where state is a USER defined type. If the user provides
> no state argument, then it defaults to unit.

I believe this is a separate issue. You are right in saying that the historic
signature, which involves lexbuf, is dubious. Following your suggestion, we
could just as well use

  parser: (state -> token * state) -> state -> ast * state

if we wish to promote a purely functional style (where values of type
state are immutable), or just

  parser: (unit -> token) -> ast

if we are willing to accept mutable state. (I am sweeping the issue of
locations under the rug; we should use token * location instead of just
token.)

That said, the historic signature 

  parser: (lexbuf -> token) -> lexbuf -> ast

is really equivalent to the previous one, in the sense that I can write
functions that convert between the two styles (see attached file).

> The point again is that the token input to the parser is infinite: it can't
> ever be an error to read a next token.

I beg to disagree. First, the input stream does not have to be infinite: if
I am reading from a file, clearly it is finite. Second, regardless of whether
the stream is finite or infinite, it *is* an error to read more tokens than
you were supposed to. If the grammar's start symbol is S, then the parser
should read a sequence of tokens that derives from S, and nothing more; it
should not overshoot and consume the first token that follows.
 
> Anyhow the problem seems to reduce to this: I think
> a parser should read the head of a stream, and Menhir
> thinks it should read the whole stream, and it uses
> the bugged lexbuf interface to test for end of stream.

As I just said in the previous paragraph, we really are in agreement: a parser
should read only a prefix of the stream.

As I said earlier, a Menhir-generated parser does not test for the physical
end of stream. It relies on the lexbuf interface only to (i) read the next
token and (ii) access the location of the current token.

> So I could fix my grammar by adding 'end-of-stream'
> to my start symbols AND hack the otherwise unused
> lexbuf to show end-of-stream after ENDMARKER is read.

Hacking the dynamic behavior of your lexbuf won't make any difference, since
end-of-stream conflicts are reported at compile time! Ponder that.

The only way of avoiding these conflicts is to change your grammar somehow.
But I still haven't understood what causes these conflicts in your grammar.
Perhaps it would be time to show it?

> Ocamlyacc doesn't complain though, suggesting it processes
> only the head of a stream .. and that there's a fundamental
> design distinction between the two parser generators.

ocamlyacc never complains. It just trusts you to know what you are doing.

-- 
François Pottier
Francois.Pottier@inria.fr
http://cristal.inria.fr/~fpottier/

[-- Attachment #2: convert.ml --]
[-- Type: text/plain, Size: 580 bytes --]

open Lexing

type ('token, 'ast) historic_parser =
    (lexbuf -> 'token) -> lexbuf -> 'ast

type ('token, 'ast) revised_parser =
    (unit -> 'token) -> 'ast

let upgrade (p : ('token, 'ast) historic_parser) : ('token, 'ast) revised_parser =
  fun (next : unit -> 'token) ->
    let (* dummy *) lexbuf =
      Lexing.from_string ""
    and lexer lexbuf =
      next()
    in
    p lexer lexbuf

let downgrade (p : ('token, 'ast) revised_parser) : ('token, 'ast) historic_parser =
  fun (lexer : lexbuf -> 'token) lexbuf ->
    let next () =
      lexer lexbuf
    in
    p next


^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [Caml-list] menhir
  2007-05-02 12:30                 ` Francois Pottier
@ 2007-05-02 16:29                   ` skaller
  2007-05-02 18:35                     ` Francois Pottier
  0 siblings, 1 reply; 20+ messages in thread
From: skaller @ 2007-05-02 16:29 UTC (permalink / raw)
  To: Francois.Pottier; +Cc: caml-list

On Wed, 2007-05-02 at 14:30 +0200, Francois Pottier wrote:
> Hello,
> 
> On Wed, May 02, 2007 at 06:41:44PM +1000, skaller wrote:
> > Exactly. In Ocamlyacc it is named 'eof', and you can use that
> > token in your productions.
> 
> As far as I know, this is incorrect. ocamlyacc does not have a predefined
> eof token. Perhaps you are thinking of ocamllex, which has an eof pattern.

I believe you're right, I apologise for confusion.

> > compilation_unit:
> >   | statement_aster ENDMARKER { $1 }
> > 
> > where no non-terminal on which statements_aster depends 
> > has a production containing ENDMARKER or # (eof).
> > 
> > Therefore, there is no conflict. When compilation_unit
> > is reduced the parser returns, the next token, whether
> > it is # or any other, is irrelevant.
> 
> Good. I seem to agree with you. Menhir should not report an end-of-stream
> conflict here. So, what does it report?

Built an LR(0) automaton with 1416 states.
Built an LR(1) automaton with 2009 states.
Warning: 145 states have an end-of-stream conflict.

Can I send you the file?

[signature of parser]

> I believe this is a separate issue. 

Yes, I agree.

> You are right in saying that the historic
> signature, which involves lexbuf, is dubious. Following your suggestion, we
> could just as well use
> 
>   parser: (state -> token * state) -> state -> ast * state
> 
> if we wish to promote a purely functional style (where values of type
> state are immutable), or just
> 
>   parser: (unit -> token) -> ast
> 
> if we are willing to accept mutable state. (I am sweeping the issue of
> locations under the rug; we should use token * location instead of just
> token.)

Or forget it, which is the approach taken by Felix: every token
contains its location: the user can organise this. This has the
advantage of not specifying a particular location format.

> That said, the historic signature 
> 
>   parser: (lexbuf -> token) -> lexbuf -> ast
> 
> is really equivalent to the previous one, in the sense that I can write
> functions that convert between the two styles (see attached file).

Yes, but you cannot write functions that take a state argument
because lexbuf is a fixed data type and there's no where to
add in any user state data.

> > The point again is that the token input to the parser is infinite: it can't
> > ever be an error to read a next token.
> 
> I beg to disagree. First, the input stream does not have to be infinite: if
> I am reading from a file, clearly it is finite.

EOF is returned an infinite number of times in C.

>  Second, regardless of whether
> the stream is finite or infinite, it *is* an error to read more tokens than
> you were supposed to. If the grammar's start symbol is S, then the parser
> should read a sequence of tokens that derives from S, and nothing more; it
> should not overshoot and consume the first token that follows.

This requires the definition: parse the *shortest* head of the
input stream.

> The only way of avoiding these conflicts is to change your grammar somehow.
> But I still haven't understood what causes these conflicts in your grammar.
> Perhaps it would be time to show it?

> ocamlyacc never complains. It just trusts you to know what you are doing.

I generate an .output file, grep for the word 'conflict',
and terminate my build if there is one found. I do not permit
any conflicts in my grammar: it's strictly unambiguous LALR(1).

It's also pure in the sense that it doesn't use crud 
like %left, %prec etc to resolve conflicts.
[The way dypgen does this is vastly superior!]


-- 
John Skaller <skaller at users dot sf dot net>
Felix, successor to C++: http://felix.sf.net


^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [Caml-list] menhir
  2007-05-02 16:29                   ` skaller
@ 2007-05-02 18:35                     ` Francois Pottier
  2007-05-03  1:30                       ` skaller
  0 siblings, 1 reply; 20+ messages in thread
From: Francois Pottier @ 2007-05-02 18:35 UTC (permalink / raw)
  To: skaller; +Cc: caml-list


On Thu, May 03, 2007 at 02:29:17AM +1000, skaller wrote:
> 
> Built an LR(0) automaton with 1416 states.
> Built an LR(1) automaton with 2009 states.
> Warning: 145 states have an end-of-stream conflict.
> 
> Can I send you the file?

Sure, please do (perhaps off the list).

> Yes, but you cannot write functions that take a state argument
> because lexbuf is a fixed data type and there's no where to
> add in any user state data.

But my point is that you never need to pass a state argument to a parser
function. Instead, you can just have the function capture the address of the
(mutable) state in its closure.

> EOF is returned an infinite number of times in C.

Point taken.

> This requires the definition: parse the *shortest* head of the
> input stream.

In fact, if there are no end-of-stream conflicts, then the head of the input
stream is *unique*, so you don't need to specify whether you are interested in
a shortest or longest prefix.

> > ocamlyacc never complains. It just trusts you to know what you are doing.
> 
> I generate an .output file, grep for the word 'conflict',
> and terminate my build if there is one found. I do not permit
> any conflicts in my grammar: it's strictly unambiguous LALR(1).

Sure, but ocamlyacc does not report end-of-stream conflicts, which I believe
are real issues...

> It's also pure in the sense that it doesn't use crud 
> like %left, %prec etc to resolve conflicts.

Good, good!

> [The way dypgen does this is vastly superior!]

How does it work?

-- 
François Pottier
Francois.Pottier@inria.fr
http://cristal.inria.fr/~fpottier/


^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [Caml-list] menhir
  2007-05-02 18:35                     ` Francois Pottier
@ 2007-05-03  1:30                       ` skaller
  2007-05-03  8:43                         ` Joel Reymont
  0 siblings, 1 reply; 20+ messages in thread
From: skaller @ 2007-05-03  1:30 UTC (permalink / raw)
  To: Francois.Pottier; +Cc: caml-list

On Wed, 2007-05-02 at 20:35 +0200, Francois Pottier wrote:
> On Thu, May 03, 2007 at 02:29:17AM +1000, skaller wrote:

> > Yes, but you cannot write functions that take a state argument
> > because lexbuf is a fixed data type and there's no where to
> > add in any user state data.
> 
> But my point is that you never need to pass a state argument to a parser
> function. Instead, you can just have the function capture the address of the
> (mutable) state in its closure.

And mine is that you can't do that because the function is
generated with a fixed signature, whose only state data
is the lexbuf.

Joel pointed out this isn't so, if you use a functor and
construct it locally, then a 'global' variable of the
functor is actually localised: I admit I didn't think of
local dynamic instantiation.

-- 
John Skaller <skaller at users dot sf dot net>
Felix, successor to C++: http://felix.sf.net


^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [Caml-list] menhir
  2007-05-03  1:30                       ` skaller
@ 2007-05-03  8:43                         ` Joel Reymont
  0 siblings, 0 replies; 20+ messages in thread
From: Joel Reymont @ 2007-05-03  8:43 UTC (permalink / raw)
  To: skaller; +Cc: Francois.Pottier, caml-list


On May 3, 2007, at 2:30 AM, skaller wrote:

> Joel pointed out this isn't so, if you use a functor and
> construct it locally, then a 'global' variable of the
> functor is actually localised: I admit I didn't think of
> local dynamic instantiation.

Just to give credit where it's due, it wasn't my idea. I butted my  
head against this issue for a few days until Francois solved it for  
me by offering the local module example.

	Thanks, Joel

--
http://wagerlabs.com/






^ permalink raw reply	[flat|nested] 20+ messages in thread

end of thread, other threads:[~2007-05-03  8:44 UTC | newest]

Thread overview: 20+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2007-04-28 10:32 menhir skaller
2007-04-28 16:50 ` [Caml-list] menhir Francois Pottier
2007-04-28 19:47   ` Markus Mottl
2007-04-28 21:15     ` Jon Harrop
2007-04-29  4:43   ` skaller
2007-04-29  7:27     ` Christophe Raffalli
2007-05-01 15:57     ` Francois Pottier
2007-05-01 17:11       ` skaller
2007-05-01 17:34         ` Francois Pottier
2007-05-01 23:42           ` skaller
2007-05-02  5:38             ` Francois Pottier
2007-05-02  5:50               ` Francois Pottier
2007-05-02  8:41               ` skaller
2007-05-02 12:30                 ` Francois Pottier
2007-05-02 16:29                   ` skaller
2007-05-02 18:35                     ` Francois Pottier
2007-05-03  1:30                       ` skaller
2007-05-03  8:43                         ` Joel Reymont
2007-05-01 17:15       ` skaller
2007-05-01 17:31         ` Francois Pottier

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).