Austin Group Defect Tracker

Aardvark Mark IV


Viewing Issue Simple Details Jump to Notes ] Issue History ] Print ]
ID Category Severity Type Date Submitted Last Update
0001388 [1003.1(2016/18)/Issue7+TC2] Shell and Utilities Objection Omission 2020-08-11 14:17 2021-01-21 22:17
Reporter geoffclare View Status public  
Assigned To
Priority normal Resolution Open  
Status New  
Name Geoff Clare
Organization The Open Group
User Reference
Section yacc
Page Number 3456
Line Number 116732
Interp Status ---
Final Accepted Text
Summary 0001388: yacc description does not say who declares yyerror() and yylex()
Description The description of yacc talks about the functions yyerror() and yylex() in various places, but nowhere does it state who is responsible for declaring them. This means that, in practice, a portable application has to declare them in the .y file (if it does not define them) in case yacc does not provide declarations of them in the code file and the compiler that will be used treats calls to undeclared functions as an error.

It doesn't take much thought about the reasons why prototypes were added to the C language to come to the conclusion that the greatly preferable solution is for yacc to be required to supply prototypes for yyerror() and yylex() that match the yyerror() definition in its library and the yylex() definition produced by lex, even (some might say especially) if the .y file includes definitions of those functions to be used instead of the library version of yyerror() and a lex-generated yylex().

There is also no statement about a declaration of main(), but the situation for main() is quite different from the above two functions. Although in theory an application could call yacc's library version of main() from code in a .y file, it is questionable why any application (other than a test suite) would do so, in particular because that version of main() does not accept any arguments and it calls exit() -- it does not return -- and therefore is of little use recursively. An application that provides its own main() could call it recursively, but can reasonably be expected to ensure it does not call main() without previously defining or declaring it. In addition, since main() has multiple different allowed prototypes, if yacc were to output a declaration it would have to be a non-prototype one:

int main();

so that there is no risk of a clash with a definition of main() that has a different prototype, but there does not seem much point in it producing such a declaration. (Or it could check whether the .y file contains a definition of main() and output a prototype declaration if it does not contain one, but that seems impractical.)

The simplest solution is just not to allow yacc to provide a declaration of main().
Desired Action On page 3456 line 116732 section yacc (Code File), change:
It also shall contain a copy of the #define statements in the header file.
to:
It also shall contain prototype declarations of the yyerror() and yylex() functions, and a copy of the #define statements in the header file, prior to any code copied from within <tt>%{</tt> and <tt>%}</tt> in the declarations section in grammar.

On page 3456 line 116734 section yacc (Code File), add a new paragraph:
The code file shall not contain a declaration of the main() function, unless one is present within <tt>%{</tt> and <tt>%}</tt> in the declarations section in grammar.

On page 3469 line 117335 section yacc (RATIONALE), add new paragraphs:
Earlier versions of this standard did not require the code file created by yacc to contain declarations of yyerror() and yylex(). This meant that portable applications that did not define them had to declare them in the grammar file, to ensure they would not be diagnosed by the compiler as being called without being declared, but this was not stated in those versions of the standard either. The standard developers decided it was preferable for yacc to include the declarations in the code file and this is now a requirement.

Earlier versions of this standard were also silent about a declaration of main(). However, the equivalent solution was not adopted since a declaration of main() would only be needed if it is called recursively by an application. Although in theory an application could call the yacc library version of main() from code in a grammar file, it is questionable why any application (other than a test suite) would do so, in particular because that version of main() does not accept any arguments and it calls exit() -- it does not return -- and therefore is of little use recursively. An application that includes its own definition of main() could call it recursively, but can reasonably be expected to ensure it does not call main() without previously defining or declaring it. An additional complication is that main() has multiple different allowed prototypes. The standard developers decided the simplest solution was not to allow yacc to provide a declaration of main() in the code file.

Tags No tags attached.
Attached Files

- Relationships

-  Notes
(0004919)
shware_systems (reporter)
2020-08-11 16:53

A collateral issue is ensuring yyin is declared as a FILE *, if yyparse() is linked to a yylex() generated by lex, in a manner accessible to code calling yyparse(). This adds an implied requirement on the .c or .h file produced to do a #include <stdio.h> to get the defining declaration of FILE.

The example main() for lex assumes such a declaration of yyin is produced at the top or bottom of the %% section, but there is no requirement this be an extern the header produced by yacc could reference. The lex description just has it shall be used, nothing on how it is to be declared, actually. The main() example for yacc doesn't declare or assign stdin to it either, assuming yylex() does this by default somehow on first call if generated by lex, or accesses stdin directly in a yylex() implemented in the programs section of the .y file.

Maybe this has been addressed as part of some other bug, but I don't remember any such discussion offhand.
(0004928)
Konrad_Schwarz (reporter)
2020-08-19 10:08

Aren't default implementations of these symbols defined in the -ly library?
(0004931)
shware_systems (reporter)
2020-08-19 16:42
edited on: 2020-08-19 16:44

Yes, but when a compiler requires the absence of any main() to be found to format an a.out as a dynamic library, rather than a utility executable, the use of liby and libl is precluded so the compile doesn't see the main() in those libraries. As such, the output of yacc or lex needs to be compile-able in a manner that doesn't require any library to provide those prototypes implicitly.

(0005205)
rhansen (manager)
2021-01-21 17:17

On page 3456 line 116732 section yacc (Code File), change:
It also shall contain a copy of the #define statements in the header file.
to:
It also shall contain function prototypes for the yyerror(), yylex(), and yyparse() functions, and a copy of the #define statements in the header file, prior to any code copied from within <tt>%{</tt> and <tt>%}</tt> in the declarations section in grammar.


On page 3456 line 116734 section yacc (Code File), add a new paragraph:
The code file shall not contain a declaration of the main() function, unless one is present within <tt>%{</tt> and <tt>%}</tt> in the declarations section in grammar.


On page 3469 line 117335 section yacc (RATIONALE), add new paragraphs:
Earlier versions of this standard did not require the code file created by yacc to contain declarations of yyerror(), yylex(), and yyparse(). This meant that portable applications that did not define them had to declare them in the grammar file, to ensure they would not be diagnosed by the compiler as being called without being declared, but this was not stated in those versions of the standard either. The standard developers decided it was preferable for yacc to include the declarations in the code file and this is now a requirement.

Earlier versions of this standard were also silent about a declaration of main(). However, the equivalent solution was not adopted because a declaration of main() would only be needed if it is called recursively by an application. Although in theory an application could call the yacc library version of main() from code in a grammar file, it is questionable why any application (other than a test suite) would do so, in particular because that version of main() does not accept any arguments and it calls exit()—it does not return—and therefore is of little use recursively. An application that includes its own definition of main() could call it recursively, but can reasonably be expected to ensure it does not call main() without previously defining or declaring it. An additional complication is that main() has multiple different allowed prototypes. The standard developers decided the simplest solution was to disallow yacc from providing a declaration of main() in the code file.
(0005206)
nick (manager)
2021-01-21 21:17

From Akim Demaille (maintainer of GNU Bison):


I agree that yyparse should be part of the declarations in the header
file (or in the implementation file if there is no header).

However I dislike that yylex and yyerror be prototyped. We don't do
it in Bison because:

- the user might decide that yylex be static, and it's actually not
  unfrequent that it is. The user might want yylex to return an enum
  of the valid expected tokens. The parser itself simply does not
  care, and forcing a prototype onto the user will just be more
  constraints for her. She might even have played with #define
  yylex() to pass additional arguments, or re#defined yylex to foolex,
  etc.

  In my humble opinion the parser is merely a consumer of
  yylex, it is not the provider. It is up to the provider to provide
  the prototype. For instance *lex* should be in charge of declaring
  yylex, not Yacc.

- the user provides her yyerror and she is perfectly welcome to
  make it a variadic function that starts with a format string,
  instead of a plain string. The parser itself only calls this
  function with a single argument, but what if the user wanted
  something more generic for some other calls to yyerror that she
  wrote herself.

  Granted, there is something dangerous here if the parser was to
  pass a message with percent-signs in it. But Yacc does not
  issue such messages.

  Again, Yacc does not provide this function, it *requires* it.
  So I disagree that Yacc should provide the prototype.

Pushing prototypes onto the user restricts her freedom. Her
freedom to make things static, her freedom to use attributes
to defeat warnings about ignored arguments, her freedom to
simply comply with a contract the way she wants. Call it
duck-typing if you want, but my opinion is that the contract
between the user and yyparse is much weaker than what a
prototype would actually enforce.

(Not to mention things that are outside the scope of Yacc,
such as passing additional arguments to yylex to avoid, for
instance, the use of globals for semantic values. But yes,
this out of the scope of Yacc.)


Note that since people are currently providing the prototypes
themselves, there's a good chance that the ones provided by
new Yaccs might be incompatible. There will be backward
compatibility issues. And portability issues when from one
machine to another you get–or don't get–the prototype,
because of varying versions of Yacc.



The routines provided in the library are merely an instance of
the set of possibles. And to be clear, I think -ly is vastly
useless. It is simply not worth the trouble to have to find a
library for main and yyerror. Someone who is ready to
spend time learning Yacc to generate a parser for a grammar is
certainly competent enough to write main and yyerror.


I also see references to yyin. I'm not sure I understand
exactly what is suggested, but yyin is out of scope. Yacc
does not care about yyin at all. yyparse knows nothing
about chars, it only knows about tokens. People routinely
make parser of strings, not just FILE*. yyin is irrelevant
to Yacc and should not be referred to in Yacc's specifications.
(0005207)
dickey (reporter)
2021-01-21 22:17

Without a prototype, a C compiler cannot provide useful diagnostics when the number or type of parameters differs from what yacc requires.

Deciding whether to require that a prototype be static or extern is outside the scope of this question, because it's possible to provide prototyping information conditionally, e.g., by a macro which defines the return type and parameters. This is an example of what has been defined by Berkeley yacc starting in 2010:

/* Parameters sent to lex. */
#ifdef YYLEX_PARAM
# define YYLEX_DECL() yylex(void *YYLEX_PARAM)
# define YYLEX yylex(YYLEX_PARAM)
#else
# define YYLEX_DECL() yylex(void)
# define YYLEX yylex()
#endif

/* Parameters sent to yyerror. */
#ifndef YYERROR_DECL
#define YYERROR_DECL() yyerror(const char *s)
#endif
#ifndef YYERROR_CALL
#define YYERROR_CALL(msg) yyerror(msg)
#endif

Those are generated, and for some non-yacc features the macros differ, but provide an application developer with a way to refer to yylex and yyerror consistently with the parser's assumptions.

- Issue History
Date Modified Username Field Change
2020-08-11 14:17 geoffclare New Issue
2020-08-11 14:17 geoffclare Name => Geoff Clare
2020-08-11 14:17 geoffclare Organization => The Open Group
2020-08-11 14:17 geoffclare Section => yacc
2020-08-11 14:17 geoffclare Page Number => 3456
2020-08-11 14:17 geoffclare Line Number => 116732
2020-08-11 14:17 geoffclare Interp Status => ---
2020-08-11 16:53 shware_systems Note Added: 0004919
2020-08-19 10:08 Konrad_Schwarz Note Added: 0004928
2020-08-19 16:42 shware_systems Note Added: 0004931
2020-08-19 16:44 shware_systems Note Edited: 0004931
2021-01-21 17:17 rhansen Note Added: 0005205
2021-01-21 21:17 nick Note Added: 0005206
2021-01-21 22:17 dickey Note Added: 0005207


Mantis 1.1.6[^]
Copyright © 2000 - 2008 Mantis Group
Powered by Mantis Bugtracker