Commit db7d81c7 authored by Ronald Charles Moore's avatar Ronald Charles Moore
Browse files

imported flex/bison example

parent 9d7ba065
####################################################################
# This Makefile started out as a copy of the one in the flex manual.
# Cf. http://flex.sourceforge.net/manual/Makefiles-and-Flex.html#Makefiles-and-Flex
#
# It replaces the amazingly complex Makefile that comes with the C++calc example
# found in the Bison manual.
#
# This is Verison 0.3 of the Makefile (as of 28 April 2020).
#
####################################################################
# Make "Variables" used here
# Cf. http://www.makelinux.net/make3/make3-CHP-2-SECT-2.html
# $@ = The filename representing the target.
# $< = The filename of the first prerequisite.
# $* = The stem of the filename of the target (i.e. without .o, .cpp...)
# $^ = The names of all the prerequisites, with spaces between them.
####################################################################
# Uncomment only one of the next two lines (choose your c++ compiler)
# CC=g++
CC=clang++
LEX=flex
YACC=bison
YFLAGS=-v -d
# Where -d is important to force creation of header files (*.hh)
# but -v is not so important. It forces creatoin of the file
# parser.output which is nice to have (but not
# strictly necessary).
HEADERS=parser.hh scanner.hh
calc++ : calc++.o scanner.o parser.o driver.o
calc++.o : calc++.cc driver.hh parser.hh
%.o: %.cc
$(CC) $(CFLAGS) -o $@ -c $<
scanner.cc: scanner.ll
$(LEX) $(LFLAGS) -o scanner.cc scanner.ll
parser.cc parser.hh: parser.yy
$(YACC) $(YFLAGS) -o parser.cc parser.yy
clean:
$(RM) *~ *.o calc++ scanner.cc parser.cc scanner.hh parser.hh parser.output location.hh stack.hh position.hh
tests: test.sh calc++
./test.sh
# Alternative (directly on the command line):
# ./test.sh 2>&1 | less
# (to see the output before it scrolls off your screen).
# Disclaimer
The contents of this directory are taken from the Bison Manual.
See the URL below for the relevant portion of the manual (and then follow the "Up" links for the rest of the manual).
The major changes are the Makefile and the test.sh shell script.
Changes made by Prof. Ronald Moore for his students at the h_da.
See https://fbi.h-da.de/personen/ronald-moore/
or mail ronald.moore@h-da.de
Last modified: 28 April 2020.
## Quick Start
To build the software, simply run `make`.
To run the test script, either enter `make tests` or `test.sh`.
To make the output of the test script more readable, try:
```
./test.sh 2>&1 | less
```
## Build Requirements
This will only work if you have flex and bison and either gcc or clang installed.
(The Makefile will have to be changed to use gcc instead of clang -- see the Makefile).
This version of this code was most recently tested with the following versions (no guarantee that it will work for other versions):
* bison 3.5.4
* flex 2.6.4
* clang 10.0.0
* gcc 9.3.0
The remainder of this file is taken *verbatim* from the README from the Bison source code distribution. Please look at the end of this file in a normal text editor to see the invisible copyright information (among other things, otherwise hidden in comments).
# calc++ - A Flex+Bison calculator
This directory contains calc++, a Bison grammar file in C++. If you never
saw the traditional implementation in C, please first read
examples/c/lexcalc, which can be seen as a C precursor of this example.
Read the corresponding chapter in the documentation: "A Complete C++
Example". It is also available on line (maybe with a different version of
Bison):
https://www.gnu.org/software/bison/manual/html_node/A-Complete-C_002b_002b-Example.html
To use it, copy this directory into some work directory, and run `make` to
compile the executable, and try it. It is a simple calculator which accepts
several variable definitions, one per line, and then a single expression to
evaluate.
The program calc++ expects the file to parse as argument; pass `-` to read
the standard input (and then hit <Ctrl-d>, control-d, to end your input).
```
$ ./calc++ -
one := 1
two := 2
three := 3
(one + two * three) * two * three
<Ctrl-d>
42
```
You may pass `-p` to activate the parser debug traces, and `-s` to activate
the scanner's.
<!---
Local Variables:
fill-column: 76
ispell-dictionary: "american"
End:
Copyright (C) 2018-2019 Free Software Foundation, Inc.
This file is part of Bison, the GNU Compiler Compiler.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
# LocalWords: calc parsers yy MERCHANTABILITY Ctrl ispell american
--->
// See https://www.gnu.org/software/bison/manual/html_node/Calc_002b_002b-Top-Level.html
#include <iostream>
#include "driver.hh"
int
main (int argc, char *argv[])
{
int res = 0;
driver drv;
for (int i = 1; i < argc; ++i)
if (argv[i] == std::string ("-p"))
drv.trace_parsing = true;
else if (argv[i] == std::string ("-s"))
drv.trace_scanning = true;
else if (!drv.parse (argv[i]))
std::cout << drv.result << '\n';
else
res = 1;
return res;
}
// See https://www.gnu.org/software/bison/manual/html_node/Calc_002b_002b-Parsing-Driver.html
#include "driver.hh"
#include "parser.hh"
driver::driver ()
: trace_parsing (false), trace_scanning (false)
{
variables["one"] = 1;
variables["two"] = 2;
}
int
driver::parse (const std::string &f)
{
file = f;
location.initialize (&file);
scan_begin ();
yy::parser parse (*this);
parse.set_debug_level (trace_parsing);
int res = parse ();
scan_end ();
return res;
}
// See https://www.gnu.org/software/bison/manual/html_node/Calc_002b_002b-Parsing-Driver.html
#ifndef DRIVER_HH
# define DRIVER_HH
# include <string>
# include <map>
# include "parser.hh"
// Give Flex the prototype of yylex we want ...
# define YY_DECL \
yy::parser::symbol_type yylex (driver& drv)
// ... and declare it for the parser's sake.
YY_DECL;
// Conducting the whole scanning and parsing of Calc++.
class driver
{
public:
driver ();
std::map<std::string, int> variables;
int result;
// Run the parser on file F. Return 0 on success.
int parse (const std::string& f);
// The name of the file being parsed.
std::string file;
// Whether to generate parser debug traces.
bool trace_parsing;
// Handling the scanner.
void scan_begin ();
void scan_end ();
// Whether to generate scanner debug traces.
bool trace_scanning;
// The token's location used by the scanner.
yy::location location;
};
#endif // ! DRIVER_HH
// A Bison parser, made by GNU Bison 3.5.4.
// Locations for Bison parsers in C++
// Copyright (C) 2002-2015, 2018-2020 Free Software Foundation, Inc.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
// As a special exception, you may create a larger work that contains
// part or all of the Bison parser skeleton and distribute that work
// under terms of your choice, so long as that work isn't itself a
// parser generator using the skeleton or a modified version thereof
// as a parser skeleton. Alternatively, if you modify or redistribute
// the parser skeleton itself, you may (at your option) remove this
// special exception, which will cause the skeleton and the resulting
// Bison output files to be licensed under the GNU General Public
// License without this special exception.
// This special exception was added by the Free Software Foundation in
// version 2.2 of Bison.
/**
** \file location.hh
** Define the yy::location class.
*/
#ifndef YY_YY_LOCATION_HH_INCLUDED
# define YY_YY_LOCATION_HH_INCLUDED
# include <iostream>
# include <string>
# ifndef YY_NULLPTR
# if defined __cplusplus
# if 201103L <= __cplusplus
# define YY_NULLPTR nullptr
# else
# define YY_NULLPTR 0
# endif
# else
# define YY_NULLPTR ((void*)0)
# endif
# endif
namespace yy {
#line 58 "location.hh"
/// A point in a source file.
class position
{
public:
/// Type for line and column numbers.
typedef int counter_type;
/// Construct a position.
explicit position (std::string* f = YY_NULLPTR,
counter_type l = 1,
counter_type c = 1)
: filename (f)
, line (l)
, column (c)
{}
/// Initialization.
void initialize (std::string* fn = YY_NULLPTR,
counter_type l = 1,
counter_type c = 1)
{
filename = fn;
line = l;
column = c;
}
/** \name Line and Column related manipulators
** \{ */
/// (line related) Advance to the COUNT next lines.
void lines (counter_type count = 1)
{
if (count)
{
column = 1;
line = add_ (line, count, 1);
}
}
/// (column related) Advance to the COUNT next columns.
void columns (counter_type count = 1)
{
column = add_ (column, count, 1);
}
/** \} */
/// File name to which this position refers.
std::string* filename;
/// Current line number.
counter_type line;
/// Current column number.
counter_type column;
private:
/// Compute max (min, lhs+rhs).
static counter_type add_ (counter_type lhs, counter_type rhs, counter_type min)
{
return lhs + rhs < min ? min : lhs + rhs;
}
};
/// Add \a width columns, in place.
inline position&
operator+= (position& res, position::counter_type width)
{
res.columns (width);
return res;
}
/// Add \a width columns.
inline position
operator+ (position res, position::counter_type width)
{
return res += width;
}
/// Subtract \a width columns, in place.
inline position&
operator-= (position& res, position::counter_type width)
{
return res += -width;
}
/// Subtract \a width columns.
inline position
operator- (position res, position::counter_type width)
{
return res -= width;
}
/// Compare two position objects.
inline bool
operator== (const position& pos1, const position& pos2)
{
return (pos1.line == pos2.line
&& pos1.column == pos2.column
&& (pos1.filename == pos2.filename
|| (pos1.filename && pos2.filename
&& *pos1.filename == *pos2.filename)));
}
/// Compare two position objects.
inline bool
operator!= (const position& pos1, const position& pos2)
{
return !(pos1 == pos2);
}
/** \brief Intercept output stream redirection.
** \param ostr the destination output stream
** \param pos a reference to the position to redirect
*/
template <typename YYChar>
std::basic_ostream<YYChar>&
operator<< (std::basic_ostream<YYChar>& ostr, const position& pos)
{
if (pos.filename)
ostr << *pos.filename << ':';
return ostr << pos.line << '.' << pos.column;
}
/// Two points in a source file.
class location
{
public:
/// Type for line and column numbers.
typedef position::counter_type counter_type;
/// Construct a location from \a b to \a e.
location (const position& b, const position& e)
: begin (b)
, end (e)
{}
/// Construct a 0-width location in \a p.
explicit location (const position& p = position ())
: begin (p)
, end (p)
{}
/// Construct a 0-width location in \a f, \a l, \a c.
explicit location (std::string* f,
counter_type l = 1,
counter_type c = 1)
: begin (f, l, c)
, end (f, l, c)
{}
/// Initialization.
void initialize (std::string* f = YY_NULLPTR,
counter_type l = 1,
counter_type c = 1)
{
begin.initialize (f, l, c);
end = begin;
}
/** \name Line and Column related manipulators
** \{ */
public:
/// Reset initial location to final location.
void step ()
{
begin = end;
}
/// Extend the current location to the COUNT next columns.
void columns (counter_type count = 1)
{
end += count;
}
/// Extend the current location to the COUNT next lines.
void lines (counter_type count = 1)
{
end.lines (count);
}
/** \} */
public:
/// Beginning of the located region.
position begin;
/// End of the located region.
position end;
};
/// Join two locations, in place.
inline location&
operator+= (location& res, const location& end)
{
res.end = end.end;
return res;
}
/// Join two locations.
inline location
operator+ (location res, const location& end)
{
return res += end;
}
/// Add \a width columns to the end position, in place.
inline location&
operator+= (location& res, location::counter_type width)
{
res.columns (width);
return res;
}
/// Add \a width columns to the end position.
inline location
operator+ (location res, location::counter_type width)
{
return res += width;
}
/// Subtract \a width columns to the end position, in place.
inline location&
operator-= (location& res, location::counter_type width)
{
return res += -width;
}
/// Subtract \a width columns to the end position.
inline location
operator- (location res, location::counter_type width)
{
return res -= width;
}
/// Compare two location objects.
inline bool
operator== (const location& loc1, const location& loc2)
{
return loc1.begin == loc2.begin && loc1.end == loc2.end;
}
/// Compare two location objects.
inline bool
operator!= (const location& loc1, const location& loc2)
{
return !(loc1 == loc2);
}
/** \brief Intercept output stream redirection.
** \param ostr the destination output stream
** \param loc a reference to the location to redirect
**
** Avoid duplicate information.
*/
template <typename YYChar>
std::basic_ostream<YYChar>&
operator<< (std::basic_ostream<YYChar>& ostr, const location& loc)
{
location::counter_type end_col
= 0 < loc.end.column ? loc.end.column - 1 : 0;
ostr << loc.begin;
if (loc.end.filename
&& (!loc.begin.filename
|| *loc.begin.filename != *loc.end.filename))
ostr << '-' << loc.end.filename << ':' << loc.end.line << '.' << end_col;
else if (loc.begin.line < loc.end.line)
ostr << '-' << loc.end.line << '.' << end_col;
else if (loc.begin.column < end_col)
ostr << '-' << end_col;
return ostr;
}
} // yy
#line 331 "location.hh"
#endif // !YY_YY_LOCATION_HH_INCLUDED
This diff is collapsed.
This diff is collapsed.
Grammar
0 $accept: unit "end of file"
1 unit: assignments exp
2 assignments: %empty
3 | assignments assignment
4 assignment: "identifier" ":=" exp
5 exp: "number"
6 | "identifier"
7 | exp "+" exp
8 | exp "-" exp
9 | exp "*" exp
10 | exp "/" exp
11 | "(" exp ")"
Terminals, with rules where they appear
"end of file" (0) 0
error (256)
":=" (258) 4
"-" (259) 8
"+" (260) 7
"*" (261) 9
"/" (262) 10
"(" (263) 11
")" (264) 11
"identifier" <std::string> (265) 4 6
"number" <int> (266) 5
Nonterminals, with rules where they appear
$accept (12)
on left: 0
exp <int> (13)
on left: 5 6 7 8 9 10 11