Related articles |
---|
multi-language parsing by using yacc pliang@msmail4.HAC.COM (Peter Liang) (1995-08-13) |
Re: multi-language parsing by using yacc simmons@bnr.ca (steve (s.s.) simmons) (1995-08-17) |
Re: multi-language parsing by using yacc erik@kroete2.freinet.de (1995-08-21) |
Re: multi-language parsing by using yacc ctv@cs.vu.nl (Cees Visser) (1995-08-21) |
Re: multi-language parsing by using yacc bobduff@world.std.com (1995-08-21) |
multi-language parsing by using yacc 75066.3204@CompuServe.COM (Carl Barron) (1995-08-22) |
Newsgroups: | comp.compilers |
From: | Cees Visser <ctv@cs.vu.nl> |
Keywords: | yacc |
Organization: | Compilers Central |
References: | 95-08-097 |
Date: | Mon, 21 Aug 1995 13:22:20 GMT |
:>
:> In other words, can I create multi-instances of parsers by using yacc?
:>
Yes. If you've already decided to use lex/yacc or flex/bison you can
create multiple instances of a scanner or parser ((without the need to
know about the scanner/parser specific internal mechanisms of these
tools, only C++ issues)).
What should be done for a scanner or parser is the following:
(1) Look at the global variables in the _generated_ *.c output files
that have to be unique for (multiple) parallel running instances
of a scanner or parser, i.e. variables that need to be local to a new
C++ scanner/parser class.
(2) Write a very simple (f)lex script that removes these variable
definitions from the generated *.c files automatically and put these
variable definitions in a new Scanner/Parser class (header file).
(3) Also, let this script remove the type declarations from the
generated *.c files. These type declarations should be added to the
corresponding new Scanner/Parser class.
(4) Some global arrays (in the lex/yacc case) should be declared
static by this script.
The lex/yacc versions require a bit more work than the flex/bison
versions.
I've used lex/yacc in the past. Currently, flex/bison. This simple
approach works well for both scanner/parser combinations.
The (f)lex script for turning the Bison output automatically into a
re-usable file is something like this:
^"int[ \t]+yychar;" { /* remove ... */ }
^"YYSTYPE[ \t]+yylval;" { /* remove ... */ }
^"YYLTYPE[ \t]+yylloc;" { /* remove ... */ }
yylex { printf ("%s::yylex", scannername); }
^"int[ \t]+yynerrs;" { /* remove ... */ }
^"int[ \t]+yydebug;" { /* remove ... */ }
yyparse { printf ("%s::parse", parsername); }
The corresponding script to turn flex output automatically into a
re-usable class that can run in parallel with other scanners requires
more cut/paste work.
This approach allows you to have multiple scanner/parser combinations
for a single language system as well as for multiple language systems
to run concurrently within a single program context.
To give you an idea of a possible Parser class, I've appended a _sketch_
of a Parser class definition.
In a real program you probably want to have 4 classes: 1 class for the
aspects that are 100 percent related to the (yacc/bison) parser, 1 for
the (lex/flex) scanner, 1 for application specific parser issues, and 1
for application specific scanner issues. The inheritance scheme is in
this case like:
Parser::ApplicParser::Scanner::ApplicScannner
to give you a maximum of flexibility and re-usability.
--ctv
========= ========= ========= ========= ========= =========
Parser class example:
#ifndef SYNSCAN_H
#define SYNSCAN_H 1
class Parser : public Scanner {
private:
// YYSTYPE yylval; IN LEXICAL SCANNER
// YYLTYPE yylloc;
int yychar;
int yydebug;
int yynerrs;
public:
Parser (FILE *infile=0) : Scanner (infile)
{
yydebug = 0;
}
void params (int argc, char **argv) {
int argi = argc;
// ....
}
int parse ();
int start ()
{
return parse ();
}
void yyerror (char *msg)
{
char *msg_not_used = msg;
fprintf (stderr, ">>> line %4d: syntax error\n", yy_line_count);
fprintf (stderr, ">>> line %4d: near token : >>%s<<\n", yy_line_count, yytext);
// ....
}
};
#endif
--
Return to the
comp.compilers page.
Search the
comp.compilers archives again.