fix: noconfirm auto-selects first AUR match

This commit is contained in:
2026-05-08 11:01:02 +01:00
parent d39cdc3fd9
commit 153cca6132
8056 changed files with 1983098 additions and 779 deletions
@@ -0,0 +1,28 @@
This directory contains examples of Bison grammar files, sorted per
language.
Several of them come from the documentation, which should be installed
together with Bison. The URLs are provided for convenience.
These examples come with a README and a Makefile. Not only can they be used
to toy with Bison, they can also be starting points for your own grammars.
Please, be sure to read the C examples before looking at the other
languages, as these examples are simpler.
<!---
Local Variables:
fill-column: 76
ispell-dictionary: "american"
End:
Copyright (C) 2018-2021 Free Software Foundation, Inc.
Permission is granted to copy, distribute and/or modify this document
under the terms of the GNU Free Documentation License, Version 1.3 or
any later version published by the Free Software Foundation; with no
Invariant Sections, with no Front-Cover Texts, and with no Back-Cover
Texts. A copy of the license is included in the "GNU Free
Documentation License" file as part of this distribution.
--->
@@ -0,0 +1,21 @@
# This Makefile is designed to be simple and readable. It does not
# aim at portability. It requires GNU Make.
BISON = bison
CXX = g++
CXXFLAGS =
PROGS = simple variant variant-11
simple: CXXFLAGS = -std=c++14
variant-11: CXXFLAGS = -std=c++11
all: $(PROGS)
%.cc %.hh %.html %.gv: %.yy
$(BISON) $(BISONFLAGS) --html --graph -o $*.cc $<
%: %.cc
$(CXX) $(CXXFLAGS) -o$@ $<
clean:
rm -f $(PROGS:=.cc) $(PROGS)
@@ -0,0 +1,68 @@
# Examples in C++
This directory contains examples of Bison grammar files in C++.
You can run `make` to compile these examples. And `make clean` to tidy
afterwards.
## simple.yy - Simple example in C++14
A very simple example in C++, based on variants and symbol constructors.
Variants allow to use any C++ type as semantic value type, and symbol
constructors ensure consistency between declared token kind and effective
semantic value.
Run as `./simple`.
Extracted from the documentation: [A Simple C++
Example](https://www.gnu.org/software/bison/manual/html_node/A-Simple-C_002b_002b-Example.html).
## variant.yy - Self-contained example in C++98
A variation of simple.yy, in C++98.
Run as `./variant`.
## variant-11.yy - Self-contained example in modern C++
Another variation of simple.yy, closely related to the previous one, but
exhibiting support for C++11's move semantics.
Run as `./variant` or `./variant NUMBER`.
## calc++ - A Complete C++ Example
A fully featured C++ version of the canonical example for parsers: a
calculator. Also uses Flex for the scanner.
Don't look at this example first: it is fully featured and can serve as a
starting point for a clean parser in C++. The previous examples are better
introductory examples, and the C examples are also useful introductory
examples.
Extracted from the documentation: [A Complete C++
Example](https://www.gnu.org/software/bison/manual/html_node/A-Complete-C_002b_002b-Example.html).
## glr
This example demonstrates the use of GLR parsers to handle (local)
ambiguities in the C++ language. See the node "Merging GLR Parses" in
Bison's documentation.
It uses (Bison) variants to store objects as semantic values. It also
demonstrates custom error messages in C++.
<!---
Local Variables:
fill-column: 76
ispell-dictionary: "american"
End:
Copyright (C) 2018-2021 Free Software Foundation, Inc.
Permission is granted to copy, distribute and/or modify this document
under the terms of the GNU Free Documentation License, Version 1.3 or
any later version published by the Free Software Foundation; with no
Invariant Sections, with no Front-Cover Texts, and with no Back-Cover
Texts. A copy of the license is included in the "GNU Free
Documentation License" file as part of this distribution.
LocalWords: mfcalc calc parsers yy ispell american
--->
@@ -0,0 +1,36 @@
# This Makefile is designed to be simple and readable. It does not
# aim at portability. It requires GNU Make.
BASE = calc++
BISON = bison
CXX = g++
FLEX = flex
all: $(BASE)
%.cc %.hh %.html %.gv: %.yy
$(BISON) $(BISONFLAGS) --html --graph -o $*.cc $<
%.cc: %.ll
$(FLEX) $(FLEXFLAGS) -o$@ $<
%.o: %.cc
$(CXX) $(CXXFLAGS) -c -o$@ $<
$(BASE): $(BASE).o driver.o parser.o scanner.o
$(CXX) -o $@ $^
$(BASE).o: parser.hh
parser.o: parser.hh
scanner.o: parser.hh
run: $(BASE)
@echo "Type arithmetic expressions. Quit with ctrl-d."
./$< -
CLEANFILES = \
$(BASE) *.o \
parser.hh parser.cc parser.output parser.xml parser.html parser.gv location.hh \
scanner.cc
clean:
rm -f $(CLEANFILES)
@@ -0,0 +1,49 @@
# 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](https://www.gnu.org/software/bison/manual/html_node/A-Complete-C_002b_002b-Example.html)
(maybe with a different version of Bison).
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-2021 Free Software Foundation, Inc.
Permission is granted to copy, distribute and/or modify this document
under the terms of the GNU Free Documentation License, Version 1.3 or
any later version published by the Free Software Foundation; with no
Invariant Sections, with no Front-Cover Texts, and with no Back-Cover
Texts. A copy of the license is included in the "GNU Free
Documentation License" file as part of this distribution.
LocalWords: calc parsers yy MERCHANTABILITY Ctrl ispell american
--->
@@ -0,0 +1,38 @@
/* Main for calc++. -*- C++ -*-
Copyright (C) 2005-2015, 2018-2021 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 <https://www.gnu.org/licenses/>. */
#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;
}
@@ -0,0 +1,65 @@
#! /bin/sh
# Copyright (C) 2005-2015, 2018-2021 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 <https://www.gnu.org/licenses/>.
cat >input <<EOF
toto := 1
toto
EOF
run 0 1
run -noerr 0 1 -s
cat >input <<EOF
a := 1
b := 2
c := 3
d := a + b * c
d
EOF
run 0 7
run -noerr 0 7 -p
cat >input <<EOF
a := 1
b := 2
c := 3
d := (a + b) * c
d
EOF
run 0 9
cat >input <<EOF
1 +
EOF
run 1 'err: -:2.1: syntax error, unexpected end of file, expecting ( or identifier or number'
# LAC finds many more tokens.
cat >input <<EOF
a := 1
d := a + b * c
EOF
run 1 'err: -:3.1: syntax error, unexpected end of file'
cat >input <<EOF
a := 072101108108111044032119111114108100033
a
EOF
run 1 'err: -:1.6-44: integer is out of range: 072101108108111044032119111114108100033'
@@ -0,0 +1,41 @@
/* Driver for calc++. -*- C++ -*-
Copyright (C) 2005-2015, 2018-2021 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 <https://www.gnu.org/licenses/>. */
#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;
}
@@ -0,0 +1,57 @@
/* Driver for calc++. -*- C++ -*-
Copyright (C) 2005-2015, 2018-2021 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 <https://www.gnu.org/licenses/>. */
#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
@@ -0,0 +1,83 @@
## Copyright (C) 2005-2006, 2008-2015, 2018-2021 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 <https://www.gnu.org/licenses/>.
## ------------------- ##
## Parser generation. ##
## ------------------- ##
SUFFIXES += .yy .stamp
.yy.stamp:
$(AM_V_YACC)rm -f $@
$(AM_V_at)touch $@.tmp
$(AM_V_at)$(YACCCOMPILE) -o $*.cc $<
$(AM_V_at)mv -f $@.tmp $@
%D%/parser.stamp: $(dependencies)
$(calcxx_sources_generated): %D%/parser.stamp
@test -f $@ || rm -f %D%/parser.stamp
@test -f $@ || $(MAKE) $(AM_MAKEFLAGS) %D%/parser.stamp
CLEANFILES += \
$(calcxx_sources_generated) \
%D%/parser.gv \
%D%/parser.output \
%D%/parser.stamp \
%D%/scanner.cc
CLEANDIRS += %D%/*.dSYM
## -------------------- ##
## Building & testing. ##
## -------------------- ##
# Avoid using BUILT_SOURCES which is too global.
$(%C%_calc___OBJECTS): $(calcxx_sources_generated)
calcxx_sources_extracted = \
%D%/driver.cc \
%D%/driver.hh \
%D%/scanner.ll \
%D%/calc++.cc
calcxx_extracted = \
$(calcxx_sources_extracted) \
%D%/parser.yy
extracted += $(calcxx_extracted)
calcxx_sources_generated = \
%D%/parser.cc \
%D%/parser.hh \
%D%/location.hh
calcxx_sources = \
$(calcxx_sources_extracted) \
$(calcxx_sources_generated)
if FLEX_CXX_WORKS
if ENABLE_CXX
check_PROGRAMS += %D%/calc++
nodist_%C%_calc___SOURCES = $(calcxx_sources)
# Don't use gnulib's system headers.
%C%_calc___CPPFLAGS = -I$(top_srcdir)/%D% -I$(top_builddir)/%D%
%C%_calc___CXXFLAGS = $(AM_CXXFLAGS) $(FLEX_SCANNER_CXXFLAGS)
TESTS += %D%/calc++.test
endif ENABLE_CXX
endif FLEX_CXX_WORKS
EXTRA_DIST += %D%/calc++.test
## ------------ ##
## Installing. ##
## ------------ ##
calcxxdir = $(docdir)/%D%
calcxx_DATA = $(calcxx_extracted)
dist_calcxx_DATA = %D%/README.md %D%/Makefile
@@ -0,0 +1,92 @@
/* Parser for calc++. -*- C++ -*-
Copyright (C) 2005-2015, 2018-2021 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 <https://www.gnu.org/licenses/>. */
%skeleton "lalr1.cc" // -*- C++ -*-
%require "3.8.2"
%header
%define api.token.raw
%define api.token.constructor
%define api.value.type variant
%define parse.assert
%code requires {
# include <string>
class driver;
}
// The parsing context.
%param { driver& drv }
%locations
%define parse.trace
%define parse.error detailed
%define parse.lac full
%code {
# include "driver.hh"
}
%define api.token.prefix {TOK_}
%token
ASSIGN ":="
MINUS "-"
PLUS "+"
STAR "*"
SLASH "/"
LPAREN "("
RPAREN ")"
;
%token <std::string> IDENTIFIER "identifier"
%token <int> NUMBER "number"
%nterm <int> exp
%printer { yyo << $$; } <*>;
%%
%start unit;
unit: assignments exp { drv.result = $2; };
assignments:
%empty {}
| assignments assignment {};
assignment:
"identifier" ":=" exp { drv.variables[$1] = $3; };
%left "+" "-";
%left "*" "/";
exp:
"number"
| "identifier" { $$ = drv.variables[$1]; }
| exp "+" exp { $$ = $1 + $3; }
| exp "-" exp { $$ = $1 - $3; }
| exp "*" exp { $$ = $1 * $3; }
| exp "/" exp { $$ = $1 / $3; }
| "(" exp ")" { $$ = $2; }
%%
void
yy::parser::error (const location_type& l, const std::string& m)
{
std::cerr << l << ": " << m << '\n';
}
@@ -0,0 +1,171 @@
/* Scanner for calc++. -*- C++ -*-
Copyright (C) 2005-2015, 2018-2021 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 <https://www.gnu.org/licenses/>. */
%{ /* -*- C++ -*- */
# include <cerrno>
# include <climits>
# include <cstdlib>
# include <cstring> // strerror
# include <string>
# include "driver.hh"
# include "parser.hh"
%}
%{
#if defined __clang__
# define CLANG_VERSION (__clang_major__ * 100 + __clang_minor__)
#endif
// Clang and ICC like to pretend they are GCC.
#if defined __GNUC__ && !defined __clang__ && !defined __ICC
# define GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__)
#endif
// Pacify warnings in yy_init_buffer (observed with Flex 2.6.4)
// and GCC 6.4.0, 7.3.0 with -O3.
#if defined GCC_VERSION && 600 <= GCC_VERSION
# pragma GCC diagnostic ignored "-Wnull-dereference"
#endif
// This example uses Flex's C back end, yet compiles it as C++.
// So expect warnings about C style casts and NULL.
#if defined CLANG_VERSION && 500 <= CLANG_VERSION
# pragma clang diagnostic ignored "-Wold-style-cast"
# pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant"
#elif defined GCC_VERSION && 407 <= GCC_VERSION
# pragma GCC diagnostic ignored "-Wold-style-cast"
# pragma GCC diagnostic ignored "-Wzero-as-null-pointer-constant"
#endif
#define FLEX_VERSION (YY_FLEX_MAJOR_VERSION * 100 + YY_FLEX_MINOR_VERSION)
// Old versions of Flex (2.5.35) generate an incomplete documentation comment.
//
// In file included from src/scan-code-c.c:3:
// src/scan-code.c:2198:21: error: empty paragraph passed to '@param' command
// [-Werror,-Wdocumentation]
// * @param line_number
// ~~~~~~~~~~~~~~~~~^
// 1 error generated.
#if FLEX_VERSION < 206 && defined CLANG_VERSION
# pragma clang diagnostic ignored "-Wdocumentation"
#endif
// Old versions of Flex (2.5.35) use 'register'. Warnings introduced in
// GCC 7 and Clang 6.
#if FLEX_VERSION < 206
# if defined CLANG_VERSION && 600 <= CLANG_VERSION
# pragma clang diagnostic ignored "-Wdeprecated-register"
# elif defined GCC_VERSION && 700 <= GCC_VERSION
# pragma GCC diagnostic ignored "-Wregister"
# endif
#endif
#if FLEX_VERSION < 206
# if defined CLANG_VERSION
# pragma clang diagnostic ignored "-Wconversion"
# pragma clang diagnostic ignored "-Wdocumentation"
# pragma clang diagnostic ignored "-Wshorten-64-to-32"
# pragma clang diagnostic ignored "-Wsign-conversion"
# elif defined GCC_VERSION
# pragma GCC diagnostic ignored "-Wconversion"
# pragma GCC diagnostic ignored "-Wsign-conversion"
# endif
#endif
// Flex 2.6.4, GCC 9
// warning: useless cast to type 'int' [-Wuseless-cast]
// 1361 | YY_CURRENT_BUFFER_LVALUE->yy_buf_size = (int) (new_size - 2);
// | ^
#if defined GCC_VERSION && 900 <= GCC_VERSION
# pragma GCC diagnostic ignored "-Wuseless-cast"
#endif
%}
%option noyywrap nounput noinput batch debug
%{
// A number symbol corresponding to the value in S.
yy::parser::symbol_type
make_NUMBER (const std::string &s, const yy::parser::location_type& loc);
%}
id [a-zA-Z][a-zA-Z_0-9]*
int [0-9]+
blank [ \t\r]
%{
// Code run each time a pattern is matched.
# define YY_USER_ACTION loc.columns (yyleng);
%}
%%
%{
// A handy shortcut to the location held by the driver.
yy::location& loc = drv.location;
// Code run each time yylex is called.
loc.step ();
%}
{blank}+ loc.step ();
\n+ loc.lines (yyleng); loc.step ();
"-" return yy::parser::make_MINUS (loc);
"+" return yy::parser::make_PLUS (loc);
"*" return yy::parser::make_STAR (loc);
"/" return yy::parser::make_SLASH (loc);
"(" return yy::parser::make_LPAREN (loc);
")" return yy::parser::make_RPAREN (loc);
":=" return yy::parser::make_ASSIGN (loc);
{int} return make_NUMBER (yytext, loc);
{id} return yy::parser::make_IDENTIFIER (yytext, loc);
. {
throw yy::parser::syntax_error
(loc, "invalid character: " + std::string(yytext));
}
<<EOF>> return yy::parser::make_YYEOF (loc);
%%
yy::parser::symbol_type
make_NUMBER (const std::string &s, const yy::parser::location_type& loc)
{
errno = 0;
long n = strtol (s.c_str(), NULL, 10);
if (! (INT_MIN <= n && n <= INT_MAX && errno != ERANGE))
throw yy::parser::syntax_error (loc, "integer is out of range: " + s);
return yy::parser::make_NUMBER ((int) n, loc);
}
void
driver::scan_begin ()
{
yy_flex_debug = trace_scanning;
if (file.empty () || file == "-")
yyin = stdin;
else if (!(yyin = fopen (file.c_str (), "r")))
{
std::cerr << "cannot open " << file << ": " << strerror (errno) << '\n';
exit (EXIT_FAILURE);
}
}
void
driver::scan_end ()
{
fclose (yyin);
}
@@ -0,0 +1,173 @@
/*
Copyright (C) 2020-2021 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 <https://www.gnu.org/licenses/>.
*/
#include <cstddef> // nullptr_t
#include <iostream>
#include <memory>
// Type erasure 101 <https://stackoverflow.com/a/26199467/1353549>.
class NodeInterface;
class Node
{
public:
Node (const Node& node) = default;
Node (Node&& node) = default;
Node () = default;
~Node () = default;
template <typename T,
// SFINAE block using this ctor as a copy/move ctor:
std::enable_if_t<!std::is_same<Node, std::decay_t<T>>::value, int>* = nullptr>
Node (T&& t);
Node& operator= (const Node& node) = default;
Node& operator= (Node&& node) = default;
explicit operator bool () const
{
return impl_ != nullptr;
}
std::ostream& print (std::ostream& o) const;
std::shared_ptr<NodeInterface> impl_;
};
static std::ostream&
operator<< (std::ostream& o, const Node &node)
{
return node.print (o);
}
class NodeInterface
{
public:
virtual ~NodeInterface () = default;
virtual std::ostream& print (std::ostream& o) const = 0;
};
std::ostream& Node::print (std::ostream& o) const
{
if (impl_)
impl_->print (o);
return o;
}
template <typename T,
std::enable_if_t<!std::is_same<std::nullptr_t, std::decay_t<T>>::value, int>* = nullptr>
struct NodeImpl : public NodeInterface
{
template <typename U>
explicit NodeImpl (U&& u)
: t{std::forward<U> (u)}
{}
virtual ~NodeImpl () = default;
virtual std::ostream& print (std::ostream& o) const
{
return o << t;
}
T t;
};
template <typename T,
std::enable_if_t<!std::is_same<Node, std::decay_t<T>>::value, int>*>
Node::Node (T&& t)
: impl_ (new NodeImpl<std::decay_t<T>>{std::forward<T> (t)})
{}
class Nterm
{
public:
Nterm (std::string form,
Node child0 = Node (), Node child1 = Node (), Node child2 = Node ())
: form_ (std::move (form))
{
children_[0] = child0;
children_[1] = child1;
children_[2] = child2;
}
friend std::ostream& operator<< (std::ostream& o, const Nterm& t)
{
o << t.form_;
if (t.children_[0])
{
o << '(' << t.children_[0];
if (t.children_[1])
o << ", " << t.children_[1];
if (t.children_[2])
o << ", " << t.children_[2];
o << ')';
}
return o;
}
private:
std::string form_;
Node children_[3];
};
class Term
{
public:
Term (std::string text)
: text_ (std::move (text))
{}
friend std::ostream& operator<< (std::ostream& o, const Term& t)
{
return o << t.text_;
}
private:
std::string text_;
};
#ifdef TEST
int main ()
{
Node n0;
std::cout << n0 << '\n';
Node n;
n = n0;
std::cout << n0 << '\n';
Term t1 = Term ("T");
std::cout << t1 << '\n';
n = t1;
std::cout << n << '\n';
std::cout << Nterm ("+", t1, t1) << '\n';
auto n1
= Nterm ("<OR>",
Nterm ("<declare>", Term ("T"), Term ("x")),
Nterm ("<cast>", Term ("x"), Term ("T")));
std::cout << n1 << '\n';
n = n1;
std::cout << n1 << '\n';
}
#endif
@@ -0,0 +1,50 @@
#! /bin/sh
# Copyright (C) 2020-2021 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 <https://www.gnu.org/licenses/>.
cat >input <<EOF
z + q;
T x;
T x = y;
x = y;
EOF
run 0 "\
1.0-5: +(z, q)
3.0-3: <declare>(T, x)
5.0-7: <init-declare>(T, x, y)
7.0-5: =(x, y)"
cat >input <<EOF
T (x) + y;
T (x);
T (y) = z + q;
T (y y) = z + q;
z + q;
EOF
run 0 "\
1.0-9: +(<cast>(x, T), y)
3.0-5: <OR>(<declare>(T, x), <cast>(x, T))
5.0-13: <OR>(<init-declare>(T, y, +(z, q)), =(<cast>(y, T), +(z, q)))
7.0-15: <error>
9.0-5: +(z, q)
err: 7.5: syntax error on token identifier (expected = or + or ))"
@@ -0,0 +1,257 @@
/* -*- C++ -*-
Copyright (C) 2020-2021 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 <https://www.gnu.org/licenses/>.
*/
/* Simplified C++ Type and Expression Grammar.
Written by Paul Hilfinger for Bison's test suite. */
%require "3.8"
%glr-parser
%skeleton "glr2.cc"
%define parse.assert
%define api.token.constructor
%header
%locations
%debug
// Custom error messages.
%define parse.error custom
%code requires
{
#include "ast.hh"
}
%define api.value.type variant
%code
{
#include <cassert>
#include <cctype>
#include <fstream>
#include <cstring>
// Merge two semantic values.
static Node
stmt_merge (const Node& x0, const Node& x1);
// Fetch a token.
static yy::parser::symbol_type
yylex ();
}
%expect-rr 1
%type <Node> TYPENAME ID stmt expr decl declarator
%printer { yyo << $$; } <Node>
%token
TYPENAME "typename"
ID "identifier"
SEMICOLON ";"
EQUAL "="
PLUS "+"
LPAREN "("
RPAREN ")"
%right "="
%left "+"
%%
prog : %empty
| prog stmt { std::cout << @2 << ": " << $2 << '\n'; }
;
stmt : expr ";" %merge <stmt_merge> { $$ = $1; }
| decl %merge <stmt_merge>
| error ";" { $$ = Nterm ("<error>"); }
;
expr : ID
| TYPENAME "(" expr ")" { $$ = Nterm ("<cast>", $3, $1); }
| expr "+" expr { $$ = Nterm ("+", $1, $3); }
| expr "=" expr { $$ = Nterm ("=", $1, $3); }
;
decl : TYPENAME declarator ";"
{ $$ = Nterm ("<declare>", $1, $2); }
| TYPENAME declarator "=" expr ";"
{ $$ = Nterm ("<init-declare>", $1, $2, $4); }
;
declarator
: ID
| "(" declarator ")" { $$ = $2; }
;
%%
std::istream* input = nullptr;
yy::parser::location_type loc;
/*---------.
| Parser. |
`---------*/
// Generate a custom error message.
void
yy::parser::report_syntax_error (const context& ctx) const
{
std::cerr << ctx.location () << ": syntax error";
if (!ctx.lookahead ().empty ())
std::cerr << " on token " << ctx.lookahead ().name ();
{
enum { TOKENMAX = 10 };
symbol_kind_type expected[TOKENMAX];
int n = ctx.expected_tokens (expected, TOKENMAX);
if (0 < n)
{
for (int i = 0; i < n; ++i)
std::cerr << (i == 0 ? " (expected " : " or ")
<< symbol_name (expected[i]);
std::cerr << ')';
}
}
std::cerr << '\n';
}
// Report the error to the user.
void
yy::parser::error (const location_type& l, const std::string& m)
{
std::cerr << l << ": " << m << '\n';
}
// Fetch the next token.
static yy::parser::symbol_type
yylex ()
{
while (true)
{
loc.step ();
loc += 1;
assert (!input->eof ());
switch (int c = input->get ())
{
case EOF:
return yy::parser::make_YYEOF (loc);
case '\t':
loc.end.column = (loc.end.column + 7) & ~7;
loc.step ();
break;
case ' ': case '\f':
loc.step ();
break;
case '\n':
loc.lines (1);
loc.end.column = 0;
loc.step ();
break;
case '+':
return yy::parser::make_PLUS (loc);
case '=':
return yy::parser::make_EQUAL (loc);
case '(':
return yy::parser::make_LPAREN (loc);
case ')':
return yy::parser::make_RPAREN (loc);
case ';':
return yy::parser::make_SEMICOLON (loc);
default:
if (isalpha (c))
{
std::string form;
do
{
form += static_cast<char> (c);
loc += 1;
c = input->get ();
}
while (isalnum (c) || c == '_');
input->unget ();
loc -= 1;
if (isupper (static_cast <unsigned char> (form[0])))
return yy::parser::make_TYPENAME (Term (form), loc);
else
return yy::parser::make_ID (Term (form), loc);
}
else
{
auto msg = "invalid character: " + std::string(1, static_cast<char> (c));
throw yy::parser::syntax_error (loc, msg);
}
}
}
}
// Merge two semantic values as an AST including both alternatives.
static Node
stmt_merge (const Node& x0, const Node& x1)
{
return Nterm ("<OR>", x0, x1);
}
/*-------.
| Main. |
`-------*/
// Parse `file` using parser `parse`.
int
process (yy::parser& parse, const std::string& file)
{
bool is_stdin = file == "-" || file.empty ();
if (is_stdin)
input = &std::cin;
else
input = new std::ifstream (file.c_str ());
loc.initialize (nullptr, 1, 0);
int status = parse ();
if (!is_stdin)
delete input;
return status;
}
int
main (int argc, char **argv)
{
yy::parser parse;
if (getenv ("YYDEBUG"))
parse.set_debug_level (1);
bool ran = false;
for (int i = 1; i < argc; ++i)
// Enable parse traces on option -p.
if (strcmp (argv[i], "-p") == 0)
parse.set_debug_level (1);
else
{
int status = process (parse, argv[i]);
ran = true;
if (!status)
return status;
}
if (!ran)
{
int status = process (parse, "");
if (!status)
return status;
}
return 0;
}
@@ -0,0 +1,52 @@
## Copyright (C) 2020-2021 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 <https://www.gnu.org/licenses/>.
glrxxdir = $(docdir)/%D%
## ----------- ##
## c++-types. ##
## ----------- ##
%D%/c++-types.stamp: $(dependencies)
$(nodist_%C%_c___types_SOURCES): %D%/c++-types.stamp
@test -f $@ || rm -f %D%/c++-types.stamp
@test -f $@ || $(MAKE) $(AM_MAKEFLAGS) %D%/c++-types.stamp
CLEANFILES += \
$(nodist_%C%_c___types_SOURCES) \
%D%/c++-types.stamp \
%D%/c++-types.output \
%D%/location.hh
CLEANDIRS += %D%/*.dSYM
## -------------------- ##
## Building & testing. ##
## -------------------- ##
# Avoid using BUILT_SOURCES which is too global.
$(%C%_c___types_OBJECTS): $(cxx_types_sources_generated)
if ENABLE_CXX14
check_PROGRAMS += %D%/c++-types
dist_%C%_c___types_SOURCES = \
%D%/ast.hh
nodist_%C%_c___types_SOURCES = \
%D%/c++-types.cc \
%D%/c++-types.hh
# Don't use gnulib's system headers.
%C%_c___types_CPPFLAGS = -I$(top_srcdir)/%D% -I$(top_builddir)/%D%
%C%_c___types_CXXFLAGS = $(CXX14_CXXFLAGS) $(WARN_CXXFLAGS_TEST)
TESTS += %D%/c++-types.test
endif ENABLE_CXX14
EXTRA_DIST += %D%/c++-types.yy %D%/c++-types.test
@@ -0,0 +1,74 @@
## Copyright (C) 2018-2021 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 <https://www.gnu.org/licenses/>.
cxxdir = $(docdir)/%D%
include %D%/calc++/local.mk
include %D%/glr/local.mk
## -------- ##
## Simple. ##
## -------- ##
BUILT_SOURCES += $(simple_sources)
CLEANFILES += %D%/simple.[ch] %D%/simple.output
CLEANDIRS += %D%/*.dSYM
cxx_DATA = $(simple_extracted)
simple_extracted = %D%/simple.yy
simple_sources = $(simple_extracted)
extracted += $(simple_extracted)
if ENABLE_CXX11
check_PROGRAMS += %D%/simple
nodist_%C%_simple_SOURCES = $(simple_sources)
%C%_simple_CXXFLAGS = $(CXX11_CXXFLAGS) $(WARN_CXXFLAGS_TEST)
# Don't use gnulib's system headers.
%C%_simple_CPPFLAGS = -I$(top_builddir)
TESTS += %D%/simple.test
%D%/simple.cc: $(dependencies)
endif
EXTRA_DIST += %D%/simple.test
## ---------- ##
## Variants. ##
## ---------- ##
if ENABLE_CXX
check_PROGRAMS += %D%/variant
nodist_%C%_variant_SOURCES = %D%/variant.yy
# Don't use gnulib's system headers.
%C%_variant_CPPFLAGS = -I$(top_builddir)
TESTS += %D%/variant.test
%D%/variant.cc: $(dependencies)
endif
EXTRA_DIST += %D%/variant.test
if ENABLE_CXX11
check_PROGRAMS += %D%/variant-11
nodist_%C%_variant_11_SOURCES = %D%/variant-11.yy
%C%_variant_11_CXXFLAGS = $(CXX11_CXXFLAGS) $(WARN_CXXFLAGS_TEST)
# Don't use gnulib's system headers.
%C%_variant_11_CPPFLAGS = -I$(top_builddir)
TESTS += %D%/variant-11.test
%D%/variant-11.cc: $(dependencies)
endif
EXTRA_DIST += %D%/variant-11.test
dist_cxx_DATA = %D%/README.md %D%/Makefile %D%/variant.yy %D%/variant-11.yy
CLEANFILES += \
%D%/simple.output %D%/variant.output %D%/variant-11.output \
%D%/simple.hh %D%/variant.hh %D%/variant-11.hh
@@ -0,0 +1,4 @@
#! /bin/sh
: >input
run 0 "{I have three numbers for you., 1, 2, 3, And that's all!}"
@@ -0,0 +1,99 @@
/* Simple variant-based parser. -*- C++ -*-
Copyright (C) 2018-2021 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 <https://www.gnu.org/licenses/>. */
%require "3.2"
%language "c++"
%define api.value.type variant
%code
{
// Print a list of strings.
auto
operator<< (std::ostream& o, const std::vector<std::string>& ss)
-> std::ostream&
{
o << '{';
const char *sep = "";
for (const auto& s: ss)
{
o << sep << s;
sep = ", ";
}
return o << '}';
}
}
%define api.token.constructor
%code
{
namespace yy
{
// Return the next token.
auto yylex () -> parser::symbol_type
{
static int count = 0;
switch (int stage = count++)
{
case 0:
return parser::make_TEXT ("I have three numbers for you.");
case 1: case 2: case 3:
return parser::make_NUMBER (stage);
case 4:
return parser::make_TEXT ("And that's all!");
default:
return parser::make_YYEOF ();
}
}
}
}
%%
result:
list { std::cout << $1 << '\n'; }
;
%nterm <std::vector<std::string>> list;
list:
%empty { /* Generates an empty string list */ }
| list item { $$ = $1; $$.push_back ($2); }
;
%nterm <std::string> item;
%token <std::string> TEXT;
%token <int> NUMBER;
item:
TEXT
| NUMBER { $$ = std::to_string ($1); }
;
%%
namespace yy
{
// Report an error to the user.
auto parser::error (const std::string& msg) -> void
{
std::cerr << msg << '\n';
}
}
int main ()
{
yy::parser parse;
return parse ();
}
@@ -0,0 +1,21 @@
#! /bin/sh
# Copyright (C) 2018-2021 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 <https://www.gnu.org/licenses/>.
: >input
run 0 "{I have numbers for you., 1, 2, 3, And that's all!}" 4
run 0 "{I have numbers for you., 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, 295, 296, 297, 298, 299, 300, 301, 302, 303, 304, 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, 325, 326, 327, 328, 329, 330, 331, 332, 333, 334, 335, 336, 337, 338, 339, 340, 341, 342, 343, 344, 345, 346, 347, 348, 349, 350, 351, 352, 353, 354, 355, 356, 357, 358, 359, 360, 361, 362, 363, 364, 365, 366, 367, 368, 369, 370, 371, 372, 373, 374, 375, 376, 377, 378, 379, 380, 381, 382, 383, 384, 385, 386, 387, 388, 389, 390, 391, 392, 393, 394, 395, 396, 397, 398, 399, 400, 401, 402, 403, 404, 405, 406, 407, 408, 409, 410, 411, 412, 413, 414, 415, 416, 417, 418, 419, 420, 421, 422, 423, 424, 425, 426, 427, 428, 429, 430, 431, 432, 433, 434, 435, 436, 437, 438, 439, 440, 441, 442, 443, 444, 445, 446, 447, 448, 449, 450, 451, 452, 453, 454, 455, 456, 457, 458, 459, 460, 461, 462, 463, 464, 465, 466, 467, 468, 469, 470, 471, 472, 473, 474, 475, 476, 477, 478, 479, 480, 481, 482, 483, 484, 485, 486, 487, 488, 489, 490, 491, 492, 493, 494, 495, 496, 497, 498, 499, 500, 501, 502, 503, 504, 505, 506, 507, 508, 509, 510, 511, 512, 513, 514, 515, 516, 517, 518, 519, 520, 521, 522, 523, 524, 525, 526, 527, 528, 529, 530, 531, 532, 533, 534, 535, 536, 537, 538, 539, 540, 541, 542, 543, 544, 545, 546, 547, 548, 549, 550, 551, 552, 553, 554, 555, 556, 557, 558, 559, 560, 561, 562, 563, 564, 565, 566, 567, 568, 569, 570, 571, 572, 573, 574, 575, 576, 577, 578, 579, 580, 581, 582, 583, 584, 585, 586, 587, 588, 589, 590, 591, 592, 593, 594, 595, 596, 597, 598, 599, 600, 601, 602, 603, 604, 605, 606, 607, 608, 609, 610, 611, 612, 613, 614, 615, 616, 617, 618, 619, 620, 621, 622, 623, 624, 625, 626, 627, 628, 629, 630, 631, 632, 633, 634, 635, 636, 637, 638, 639, 640, 641, 642, 643, 644, 645, 646, 647, 648, 649, 650, 651, 652, 653, 654, 655, 656, 657, 658, 659, 660, 661, 662, 663, 664, 665, 666, 667, 668, 669, 670, 671, 672, 673, 674, 675, 676, 677, 678, 679, 680, 681, 682, 683, 684, 685, 686, 687, 688, 689, 690, 691, 692, 693, 694, 695, 696, 697, 698, 699, 700, 701, 702, 703, 704, 705, 706, 707, 708, 709, 710, 711, 712, 713, 714, 715, 716, 717, 718, 719, 720, 721, 722, 723, 724, 725, 726, 727, 728, 729, 730, 731, 732, 733, 734, 735, 736, 737, 738, 739, 740, 741, 742, 743, 744, 745, 746, 747, 748, 749, 750, 751, 752, 753, 754, 755, 756, 757, 758, 759, 760, 761, 762, 763, 764, 765, 766, 767, 768, 769, 770, 771, 772, 773, 774, 775, 776, 777, 778, 779, 780, 781, 782, 783, 784, 785, 786, 787, 788, 789, 790, 791, 792, 793, 794, 795, 796, 797, 798, 799, 800, 801, 802, 803, 804, 805, 806, 807, 808, 809, 810, 811, 812, 813, 814, 815, 816, 817, 818, 819, 820, 821, 822, 823, 824, 825, 826, 827, 828, 829, 830, 831, 832, 833, 834, 835, 836, 837, 838, 839, 840, 841, 842, 843, 844, 845, 846, 847, 848, 849, 850, 851, 852, 853, 854, 855, 856, 857, 858, 859, 860, 861, 862, 863, 864, 865, 866, 867, 868, 869, 870, 871, 872, 873, 874, 875, 876, 877, 878, 879, 880, 881, 882, 883, 884, 885, 886, 887, 888, 889, 890, 891, 892, 893, 894, 895, 896, 897, 898, 899, 900, 901, 902, 903, 904, 905, 906, 907, 908, 909, 910, 911, 912, 913, 914, 915, 916, 917, 918, 919, 920, 921, 922, 923, 924, 925, 926, 927, 928, 929, 930, 931, 932, 933, 934, 935, 936, 937, 938, 939, 940, 941, 942, 943, 944, 945, 946, 947, 948, 949, 950, 951, 952, 953, 954, 955, 956, 957, 958, 959, 960, 961, 962, 963, 964, 965, 966, 967, 968, 969, 970, 971, 972, 973, 974, 975, 976, 977, 978, 979, 980, 981, 982, 983, 984, 985, 986, 987, 988, 989, 990, 991, 992, 993, 994, 995, 996, 997, 998, 999, And that's all!}" 1000
@@ -0,0 +1,154 @@
/*
Copyright (C) 2008-2015, 2018-2021 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 <https://www.gnu.org/licenses/>.
*/
%require "3.2"
%debug
%language "c++"
%define api.token.constructor
%define api.value.type variant
%define api.value.automove
%define api.location.file none
%define parse.assert
%locations
%code requires // *.hh
{
#include <memory> // std::unique_ptr
#include <string>
#include <vector>
using string_uptr = std::unique_ptr<std::string>;
using string_uptrs = std::vector<string_uptr>;
}
%code // *.cc
{
#include <climits> // INT_MIN, INT_MAX
#include <iostream>
#include <sstream>
namespace yy
{
// Prototype of the yylex function providing subsequent tokens.
static parser::symbol_type yylex ();
// Print a vector of strings.
std::ostream&
operator<< (std::ostream& o, const string_uptrs& ss)
{
o << '{';
const char *sep = "";
for (const auto& s: ss)
{
o << sep << *s;
sep = ", ";
}
return o << '}';
}
}
template <typename... Args>
string_uptr
make_string_uptr (Args&&... args)
{
// std::make_unique is C++14.
return string_uptr (new std::string{std::forward<Args> (args)...});
}
}
%token <string_uptr> TEXT;
%token <int> NUMBER;
%printer { yyo << '(' << &$$ << ") " << $$; } <*>;
%printer { yyo << *$$; } <string_uptr>;
%token END_OF_FILE 0;
%type <string_uptr> item;
%type <string_uptrs> list;
%%
result:
list { std::cout << $1 << '\n'; }
;
list:
%empty { /* Generates an empty string list */ }
| list item { $$ = $1; $$.emplace_back ($2); }
;
item:
TEXT
| NUMBER { $$ = make_string_uptr (std::to_string ($1)); }
;
%%
// The last number return by the scanner is max - 1.
int max = 4;
namespace yy
{
// The yylex function providing subsequent tokens:
// TEXT "I have three numbers for you."
// NUMBER 1
// NUMBER 2
// NUMBER ...
// NUMBER max - 1
// TEXT "And that's all!"
// END_OF_FILE
static
parser::symbol_type
yylex ()
{
static int count = 0;
const int stage = count;
++count;
auto loc = parser::location_type{nullptr, stage + 1, stage + 1};
if (stage == 0)
return parser::make_TEXT (make_string_uptr ("I have numbers for you."), std::move (loc));
else if (stage < max)
return parser::make_NUMBER (stage, std::move (loc));
else if (stage == max)
return parser::make_TEXT (make_string_uptr ("And that's all!"), std::move (loc));
else
return parser::make_END_OF_FILE (std::move (loc));
}
// Mandatory error function
void
parser::error (const parser::location_type& loc, const std::string& msg)
{
std::cerr << loc << ": " << msg << '\n';
}
}
int
main (int argc, const char *argv[])
{
if (2 <= argc && isdigit (static_cast<unsigned char> (*argv[1])))
{
auto maxl = strtol (argv[1], nullptr, 10);
max = INT_MIN <= maxl && maxl <= INT_MAX ? int(maxl) : 4;
}
auto&& p = yy::parser{};
p.set_debug_level (!!getenv ("YYDEBUG"));
return p.parse ();
}
// Local Variables:
// mode: C++
// End:
@@ -0,0 +1,19 @@
#! /bin/sh
# Copyright (C) 2018-2021 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 <https://www.gnu.org/licenses/>.
: >input
run 0 "{I have three numbers for you., 1, 2, 3, And that's all!}"
@@ -0,0 +1,154 @@
/*
Copyright (C) 2008-2015, 2018-2021 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 <https://www.gnu.org/licenses/>.
*/
%require "3.2"
%debug
%language "c++"
%define api.token.constructor
%define api.value.type variant
%define api.location.file none
%define parse.assert
%locations
%code requires // *.hh
{
#include <string>
#include <vector>
typedef std::vector<std::string> strings_type;
}
%code // *.cc
{
#include <iostream>
#include <sstream>
namespace yy
{
// Prototype of the yylex function providing subsequent tokens.
static parser::symbol_type yylex ();
// Print a vector of strings.
std::ostream&
operator<< (std::ostream& o, const strings_type& ss)
{
o << '{';
const char *sep = "";
for (strings_type::const_iterator i = ss.begin (), end = ss.end ();
i != end; ++i)
{
o << sep << *i;
sep = ", ";
}
return o << '}';
}
}
// Convert to string.
template <typename T>
std::string
to_string (const T& t)
{
std::ostringstream o;
o << t;
return o.str ();
}
}
%token <::std::string> TEXT;
%token <int> NUMBER;
%printer { yyo << '(' << &$$ << ") " << $$; } <*>;
%token END_OF_FILE 0;
%type <::std::string> item;
%type <::std::vector<std::string>> list;
%%
result:
list { std::cout << $1 << '\n'; }
;
list:
%empty { /* Generates an empty string list */ }
| list item { std::swap ($$, $1); $$.push_back ($2); }
;
item:
TEXT
| NUMBER { $$ = to_string ($1); }
;
%%
namespace yy
{
// Use nullptr with pre-C++11.
#if !defined __cplusplus || __cplusplus < 201103L
# define NULLPTR 0
#else
# define NULLPTR nullptr
#endif
// The yylex function providing subsequent tokens:
// TEXT "I have three numbers for you."
// NUMBER 1
// NUMBER 2
// NUMBER 3
// TEXT "And that's all!"
// END_OF_FILE
static
parser::symbol_type
yylex ()
{
static int count = 0;
const int stage = count;
++count;
parser::location_type loc (NULLPTR, stage + 1, stage + 1);
switch (stage)
{
case 0:
return parser::make_TEXT ("I have three numbers for you.", loc);
case 1:
case 2:
case 3:
return parser::make_NUMBER (stage, loc);
case 4:
return parser::make_TEXT ("And that's all!", loc);
default:
return parser::make_END_OF_FILE (loc);
}
}
// Mandatory error function
void
parser::error (const parser::location_type& loc, const std::string& msg)
{
std::cerr << loc << ": " << msg << '\n';
}
}
int
main ()
{
yy::parser p;
p.set_debug_level (!!getenv ("YYDEBUG"));
return p.parse ();
}
// Local Variables:
// mode: C++
// End:
@@ -0,0 +1,99 @@
# Examples in C
This directory contains simple examples of Bison grammar files in C.
Some of them come from the documentation, which should be installed together
with Bison. The URLs are provided for convenience.
## rpcalc - Reverse Polish Notation Calculator
The first example is that of a simple double-precision Reverse Polish
Notation calculator (a calculator using postfix operators). This example
provides a good starting point, since operator precedence is not an issue.
Extracted from the documentation: [Reverse Polish Notation
Calculator](https://www.gnu.org/software/bison/manual/html_node/RPN-Calc.html).
## calc - Simple Calculator
This example is slightly more complex than rpcalc: it features infix
operators (`1 + 2`, instead of `1 2 +` in rpcalc), but it does so using a
unambiguous grammar of the arithmetic instead of using precedence
directives (%left, etc.).
## mfcalc - Multi-Function Calculator
A more complete C example: a multi-function calculator. More complex than
the previous example. Using precedence directives to support infix
operators.
Extracted from the documentation: [Multi-Function Calculator:
mfcalc](https://www.gnu.org/software/bison/manual/html_node/Multi_002dfunction-Calc.html).
## lexcalc - calculator with Flex and Bison
The calculator with precedence directives and location tracking. It uses
Flex to generate the scanner.
## reccalc - recursive calculator with Flex and Bison
This example builds on top of the previous one to provide a reentrant
parser. Such parsers can be called concurrently in different threads, or
even recursively. To demonstrate this feature, expressions in parentheses
are tokenized as strings, and then recursively parsed from the parser. So
`(((1)+(2))*((3)+(4)))` uses eight parsers, with a depth of four.
## pushcalc - calculator implemented with a push parser
All the previous examples are so called "pull parsers": the user invokes the
parser once, which repeatedly calls the scanner until the input is drained.
This example demonstrates the "push parsers": the user calls the scanner to
fetch the next token, passes it to the parser, and repeats the operation
until the input is drained.
This example is a straightforward conversion of the 'calc' example to the
push-parser model.
## bistromathic - all the bells and whistles
This example demonstrates best practices when using Bison.
- Its hand-written scanner tracks locations.
- Its interface is pure.
- It uses %params to pass user information to the parser and scanner.
- Its scanner uses the `error` token to signal lexical errors and enter
error recovery.
- Its interface is "incremental", well suited for interaction: it uses the
push-parser API to feed the parser with the incoming tokens.
- It features an interactive command line with completion based on the
parser state, based on `yyexpected_tokens`.
- It uses Bison's standard catalog for internationalization of generated
messages.
- It uses a custom syntax error with location, lookahead correction and
token internationalization.
- Error messages quote the source with squiggles that underline the error:
```
> 123 456
1.5-7: syntax error: expected end of file or + or - or * or / or ^ before number
1 | 123 456
| ^~~
```
- It supports debug traces with semantic values.
- It uses named references instead of the traditional $1, $2, etc.
<!---
Local Variables:
fill-column: 76
ispell-dictionary: "american"
End:
Copyright (C) 2018-2021 Free Software Foundation, Inc.
This file is part of GNU bison, the GNU Compiler Compiler.
Permission is granted to copy, distribute and/or modify this document
under the terms of the GNU Free Documentation License, Version 1.3 or
any later version published by the Free Software Foundation; with no
Invariant Sections, with no Front-Cover Texts, and with no Back-Cover
Texts. A copy of the license is included in the "GNU Free
Documentation License" file as part of this distribution.
LocalWords: mfcalc calc parsers yy rpcalc lexcalc redux reccalc ispell
LocalWords: reentrant tokenized american postfix pushcalc bistromathic
LocalWords: lookahead
-->
@@ -0,0 +1,35 @@
# This Makefile is designed to be simple and readable. It does not
# aim at portability. It requires GNU Make.
BASE = bistromathic
BISON = bison
# We need to find the headers and libs for readline (and possibly intl).
# You probably need to customize this for your own environment.
CPPFLAGS = -I/opt/local/include
LDFLAGS = -L/opt/local/lib
# Find the translation catalog for Bison's generated messagess.
BISON_LOCALEDIR = $(shell $(BISON) $(BISON_FLAGS) --print-localedir)
CPPFLAGS += -DENABLE_NLS -DBISON_LOCALEDIR='"$(BISON_LOCALEDIR)"'
LIBS = -lreadline -lm # In some environments, -lintl is needed.
all: $(BASE)
%.c %.h %.html %.xml %.gv: %.y
$(BISON) $(BISONFLAGS) --header --html --graph -o $*.c $<
$(BASE): parse.o
$(CC) $(CPPFLAGS) $(CFLAGS) -o $@ $^ $(LDFLAGS) $(LIBS)
run: $(BASE)
@echo "Type bistromathic expressions. Quit with ctrl-d."
./$<
CLEANFILES = \
$(BASE) *.o \
parse.[ch] parse.output parse.xml parse.html parse.gv
clean:
rm -f $(CLEANFILES)
@@ -0,0 +1,49 @@
# bistromathic - all the bells and whistles
This example demonstrates best practices when using Bison.
- Its hand-written scanner tracks locations.
- Its interface is pure.
- It uses %params to pass user information to the parser and scanner.
- Its scanner uses the `error` token to signal lexical errors and enter
error recovery.
- Its interface is "incremental", well suited for interaction: it uses the
push-parser API to feed the parser with the incoming tokens.
- It features an interactive command line with completion based on the
parser state, based on `yyexpected_tokens`.
- It uses Bison's standard catalog for internationalization of generated
messages.
- It uses a custom syntax error with location, lookahead correction and
token internationalization.
- Error messages quote the source with squiggles that underline the error:
```
> 123 456
1.5-7: syntax error: expected end of file or + or - or * or / or ^ before number
1 | 123 456
| ^~~
```
- It supports debug traces with semantic values.
- It uses named references instead of the traditional $1, $2, etc.
To customize the interaction with bistromathic, see the GNU Readline user
manual (see `info rluserman`).
<!---
Local Variables:
fill-column: 76
ispell-dictionary: "american"
End:
Copyright (C) 2020-2021 Free Software Foundation, Inc.
This file is part of Bison, the GNU Compiler Compiler.
Permission is granted to copy, distribute and/or modify this document
under the terms of the GNU Free Documentation License, Version 1.3 or
any later version published by the Free Software Foundation; with no
Invariant Sections, with no Front-Cover Texts, and with no Back-Cover
Texts. A copy of the license is included in the "GNU Free
Documentation License" file as part of this distribution.
LocalWords: bistromathic yyexpected lookahead ispell american
LocalWords: MERCHANTABILITY
--->
@@ -0,0 +1,368 @@
#! /bin/sh
# Copyright (C) 2020-2021 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 <https://www.gnu.org/licenses/>.
# Users may customize the behavior of readline, which might break our
# expected results.
INPUTRC=/dev/null
export INPUTRC
# Beware of portability issues of readline when not feeding it from a
# terminal.
#
# With recent versions of GNU Readline, input "1+2*3\n" gives
# "> 1+2*3\n7\n> \n"
#
# macOS' version does not display the prompt and does not repeat stdin
# on stdout, so input "1+2*3\n" gives "7\n" as output. Let's try to
# cope with this.
#
# On OpenBSD 6.5 the prompt is displayed, but the input is not
# repeated (!). So input "1+2*3\n" gives "> 7\n> \n" as output.
#
# On AIX, you get some escaping sequence before the prompt:
# "<ESC>[?1034h> 1+2*3". It appears to pass the terminfo capability
# "smm", to put the terminal in "meta mode": as if the user had hit
# META.
echo >perfect '> 0
0
> '
echo >ok '0'
echo '0' | prog >effective
echo "checking for readline output..."
if diff perfect effective; then
# Alles ist gut.
strip_prompt=false
elif diff ok effective; then
strip_prompt=true
else
skip "this is not the GNU Readline we expect"
fi
cat >input <<EOF
1+2*3
EOF
run 0 '> 1+2*3
7
> '
cat >input <<EOF
(1+2) * 3
EOF
run 0 '> (1+2) * 3
9
> '
run -noerr 0 '> (1+2) * 3
9
> ' -p
cat >input <<EOF
a = 256
sqrt (a)
EOF
run 0 '> a = 256
256
> sqrt (a)
16
> '
cat >input <<EOF
a = .16
b = 10 ^ 2
sqrt (a * b)
EOF
run 0 '> a = .16
0.16
> b = 10 ^ 2
100
> sqrt (a * b)
4
> '
cat >input <<EOF
*
EOF
run 0 '> *
> ''
err: 1.1: syntax error: expected end of file or - or ( or exit or number or function etc., before *
err: 1 | *
err: | ^'
# Underline long errors.
cat >input <<EOF
123 123456
EOF
run 0 '> 123 123456
> ''
err: 1.5-10: syntax error: expected end of file or + or - or * or / or ^ before number
err: 1 | 123 123456
err: | ^~~~~~'
cat >input <<EOF
1 + 2 * * 3
EOF
run 0 '> 1 + 2 * * 3
> ''
err: 1.9: syntax error: expected - or ( or number or function or variable before *
err: 1 | 1 + 2 * * 3
err: | ^'
cat >input <<EOF
1 / 0
EOF
run 0 '> 1 / 0
> ''
err: 1.1-5: error: division by zero'
## ---------------- ##
## Error recovery. ##
## ---------------- ##
cat >input <<EOF
((1 ++ 2) ** 3)
(1 ++ 2) + (3 ** 4)
EOF
run 0 '> ((1 ++ 2) ** 3)
666
> (1 ++ 2) + (3 ** 4)
1332
> ''
err: 1.6: syntax error: expected - or ( or number or function or variable before +
err: 1 | ((1 ++ 2) ** 3)
err: | ^
err: 2.5: syntax error: expected - or ( or number or function or variable before +
err: 2 | (1 ++ 2) + (3 ** 4)
err: | ^
err: 2.16: syntax error: expected - or ( or number or function or variable before *
err: 2 | (1 ++ 2) + (3 ** 4)
err: | ^'
# The rule "( error )" should work even if there are no tokens between "(" and ")".
cat >input <<EOF
()
EOF
run 0 '> ()
666
> ''
err: 1.2: syntax error: expected - or ( or number or function or variable before )
err: 1 | ()
err: | ^'
cat >input <<EOF
100% + 10
EOF
run 0 '> 100% + 10
> ''
err: 1.4: syntax error: invalid character: %'
# Traces. This allows to check the location of the error. If we
# forget to map YYerror to YYUNDEF, error recovery enters an endless
# loop with this input.
cat >input <<EOF
(+_)
EOF
run 0 '> (+_)
666
> ''
err: Starting parse
err: Entering state 0
err: Stack now 0
err: Reading a token
err: Next token is token ( (1.1: )
err: Shifting token ( (1.1: )
err: Entering state 2
err: Stack now 0 2
err: Return for a new token:
err: Reading a token
err: Next token is token + (1.2: )
err: LAC: initial context established for +
err: LAC: checking lookahead +: Err
err: LAC: checking lookahead end of file: Err
err: LAC: checking lookahead +: Err
err: LAC: checking lookahead -: S1
err: LAC: checking lookahead *: Err
err: LAC: checking lookahead /: Err
err: LAC: checking lookahead ^: Err
err: LAC: checking lookahead (: S2
err: LAC: checking lookahead ): Err
err: LAC: checking lookahead =: Err
err: LAC: checking lookahead exit: Err
err: LAC: checking lookahead number: S4
err: LAC: checking lookahead function: S5
err: LAC: checking lookahead variable: S6
err: LAC: checking lookahead NEG: Err
err: 1.2: syntax error: expected - or ( or number or function or variable before +
err: 1 | (+_)
err: | ^
err: LAC: initial context discarded due to error recovery
err: Shifting token error (1.2: )
err: Entering state 10
err: Stack now 0 2 10
err: Next token is token + (1.2: )
err: LAC: initial context established for +
err: LAC: checking lookahead +: Err
err: Error: discarding token + (1.2: )
err: Error: popping token error (1.2: )
err: Stack now 0 2
err: LAC: initial context discarded due to error recovery
err: Shifting token error (1.2: )
err: Entering state 10
err: Stack now 0 2 10
err: Return for a new token:
err: 1.3: syntax error: invalid character: _
err: Reading a token
err: Error: popping token error (1.2: )
err: Stack now 0 2
err: Shifting token error (1.2-3: )
err: Entering state 10
err: Stack now 0 2 10
err: Next token is token invalid token (1.3: )
err: LAC: initial context established for invalid token
err: LAC: checking lookahead invalid token: Always Err
err: Error: discarding token invalid token (1.3: )
err: Error: popping token error (1.2-3: )
err: Stack now 0 2
err: LAC: initial context discarded due to error recovery
err: Shifting token error (1.2-3: )
err: Entering state 10
err: Stack now 0 2 10
err: Return for a new token:
err: Reading a token
err: Next token is token ) (1.4: )
err: Shifting token ) (1.4: )
err: Entering state 20
err: Stack now 0 2 10 20
err: Reducing stack by rule XX (line XXX):
err: $1 = token ( (1.1: )
err: $2 = token error (1.2-3: )
err: $3 = token ) (1.4: )
err: -> $$ = nterm exp (1.1-4: 666)
err: Entering state 8
err: Stack now 0 8
err: Return for a new token:
err: Reading a token
err: Now at end of input.
err: LAC: initial context established for end of file
err: LAC: checking lookahead end of file: R2 G7 S14
err: Reducing stack by rule XX (line XXX):
err: $1 = nterm exp (1.1-4: 666)
err: -> $$ = nterm input (1.1-4: )
err: Entering state 7
err: Stack now 0 7
err: Now at end of input.
err: Shifting token end of file (1.5: )
err: LAC: initial context discarded due to shift
err: Entering state 14
err: Stack now 0 7 14
err: Stack now 0 7 14
err: Cleanup: popping token end of file (1.5: )
err: Cleanup: popping nterm input (1.1-4: )' -p
## ------------ ##
## Completion. ##
## ------------ ##
# From now on, the differences between versions of GNU Readline are
# too painful to try to cope with.
if $strip_prompt; then
echo "SKIP: this is not the GNU Readline we expect"
exit $status
fi
# On Windows10/MSYS2 the ^G coming from <tab> completion is not
# emitted the same way
# (https://lists.gnu.org/r/bug-bison/2020-05/msg00076.html).
echo "checking for kernel name... $(uname -s)"
case $(uname -s) in
(MSYS*)
echo "SKIP: this is Windows/MSYS"
exit $status
;;
esac
# Check completion after an operator.
sed -e 's/\\t/ /g' >input <<EOF
(1+\t\t
EOF
# Nuke the possible trailing white spaces in the effective output.
# This is to cope with some readlines that pad all the suggestions
# with white spaces (for alignment), including the last one on a line.
#
# See for instance <https://trac.macports.org/ticket/59927#comment:48>
# and its test-suite.log:
# <https://trac.macports.org/attachment/ticket/59927/bison-3.7.6-test-10.13.test-suite.log>
run -t 0 '> (1+
( - atan cos exp ln number sin sqrt
> (1+
>
err: 1.4: syntax error: expected - or ( or number or function or variable before end of file
err: 1 | (1+
err: | ^'
# Check the completion of a word.
sed -e 's/\\t/ /g' >input <<EOF
(at\t\t
EOF
run 0 '> (atan ( ''
> ''
err: 1.9: syntax error: expected - or ( or number or function or variable before end of file
err: 1 | (atan ( ''
err: | ^'
# Check the completion at the very beginning.
sed -e 's/\\t/ /g' >input <<EOF
e\t\t
EOF
run -n 0 '> e
end of file exit exp ''
> e
0
> ''
err: '
# Check that completion prints valid locations even when there is an error.
sed -e 's/\\t/ /g' >input <<EOF
1++\t
EOF
run -n 0 '> 1++ ''
> ''
err: 1.3: syntax error: expected - or ( or number or function or variable before +
err: 1 | 1++ ''
err: | ^
'
# And even when the error was recovered from.
sed -e 's/\\t/ /g' >input <<EOF
(1++2) + 3 +\t\t
EOF
run -n 0 '> (1++2) + 3 + ''
> ''
err: 1.4: syntax error: expected - or ( or number or function or variable before +
err: 1 | (1++2) + 3 + ''
err: | ^
err: 1.15: syntax error: expected - or ( or number or function or variable before end of file
err: 1 | (1++2) + 3 + ''
err: | ^
'
@@ -0,0 +1,41 @@
## Copyright (C) 2020-2021 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 <https://www.gnu.org/licenses/>.
bistromathicdir = $(docdir)/%D%
## --------------- ##
## Bistromathics. ##
## --------------- ##
%D%/parse.c: $(dependencies)
if ENABLE_BISTROMATHIC
check_PROGRAMS += %D%/bistromathic
TESTS += %D%/bistromathic.test
nodist_%C%_bistromathic_SOURCES = %D%/parse.y
# Don't use gnulib's system headers.
%C%_bistromathic_CPPFLAGS = \
-DBISON_LOCALEDIR='"$(localdir)"' \
-DLOCALEDIR='"$(localdir)"' \
-I$(top_srcdir)/%D% -I$(top_builddir)/%D%
%C%_bistromathic_CFLAGS = $(TEST_CFLAGS)
%C%_bistromathic_LDADD = -lm $(LIBREADLINE) $(LIBINTL)
endif
EXTRA_DIST += %D%/bistromathic.test
dist_bistromathic_DATA = %D%/parse.y %D%/Makefile %D%/README.md
CLEANFILES += %D%/parse.[ch] %D%/parse.output
CLEANDIRS += %D%/*.dSYM
@@ -0,0 +1,695 @@
/* Parser and scanner for bistromathic. -*- C -*-
Copyright (C) 2019-2021 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 <https://www.gnu.org/licenses/>. */
%require "3.7"
// Emitted on top of the implementation file.
%code top {
#include <ctype.h> // isdigit
#include <locale.h> // LC_ALL
#include <math.h> // cos, sin, etc.
#include <stdarg.h> // va_start
#include <stdio.h> // printf
#include <stdlib.h> // calloc
#include <string.h> // strcmp
#include <readline/readline.h>
#include <readline/history.h>
#if defined ENABLE_NLS && ENABLE_NLS
// Unable the translation of Bison's generated messages.
# define YYENABLE_NLS 1
# include <libintl.h>
// Unless specified otherwise, we expect bistromathic's own
// catalog to be installed in the same tree as Bison's catalog.
# ifndef LOCALEDIR
# define LOCALEDIR BISON_LOCALEDIR
# endif
#endif
}
// Emitted in the header file, before the definition of YYSTYPE.
%code requires {
// Function type.
typedef double (func_t) (double);
// Data type for links in the chain of symbols.
typedef struct symrec symrec;
struct symrec
{
char *name; // name of symbol
int type; // type of symbol: either VAR or FUN
union
{
double var; // value of a VAR
func_t *fun; // value of a FUN
} value;
symrec *next; // link field
};
symrec *putsym (char const *name, int sym_type);
symrec *getsym (char const *name);
// Exchanging information with the parser.
typedef struct
{
// Whether to not emit error messages.
int silent;
// The current input line.
const char *line;
} user_context;
}
// Emitted in the header file, after the definition of YYSTYPE.
%code provides {
# ifndef __attribute__
# ifndef __GNUC__
# define __attribute__(Spec) /* empty */
# endif
# endif
yytoken_kind_t
yylex (const char **line, YYSTYPE *yylval, YYLTYPE *yylloc,
const user_context *uctx);
void yyerror (const YYLTYPE *loc, const user_context *uctx,
char const *format, ...)
__attribute__ ((__format__ (__printf__, 3, 4)));
}
// Emitted in the implementation file.
%code {
// Print *LOC on OUT.
static void location_print (FILE *out, YYLTYPE const * const loc);
#define YYLOCATION_PRINT location_print
#if defined ENABLE_NLS && ENABLE_NLS
# define _(Msgid) gettext (Msgid)
#else
# define _(Msgid) (Msgid)
#endif
// Whether to quit.
int done = 0;
}
// Include the header in the implementation rather than duplicating it.
%define api.header.include {"parse.h"}
// Don't share global variables between the scanner and the parser.
%define api.pure full
// Generate a push parser.
%define api.push-pull push
// To avoid name clashes (e.g., with C's EOF) prefix token definitions
// with TOK_ (e.g., TOK_EOF).
%define api.token.prefix {TOK_}
// Customized syntax error messages (see yyreport_syntax_error)...
%define parse.error custom
// ... with locations...
%locations
// ... and accurate list of expected tokens.
%define parse.lac full
// Generate the parser description file (calc.output).
%verbose
// User information exchanged with the parser and scanner.
%param {const user_context *uctx}
// Generate YYSTYPE from the types assigned to symbols.
%define api.value.type union
%token
PLUS "+"
MINUS "-"
STAR "*"
SLASH "/"
CARET "^"
LPAREN "("
RPAREN ")"
EQUAL "="
EXIT "exit"
<double>
NUM _("number")
<symrec*>
FUN _("function")
VAR _("variable")
%nterm <double> exp
// Enable run-time traces (yydebug).
%define parse.trace
// Formatting semantic values in debug traces.
%printer { fprintf (yyo, "%s", $$->name); } VAR;
%printer { fprintf (yyo, "%s()", $$->name); } FUN;
%printer { fprintf (yyo, "%g", $$); } <double>;
// Precedence (from lowest to highest) and associativity.
%precedence "="
%left "+" "-"
%left "*" "/"
%precedence NEG // negation--unary minus
%right "^" // exponentiation
%% // The grammar follows.
input:
%empty
| exp { printf ("%.10g\n", $exp); }
| "exit" { done = 1; }
;
exp:
NUM
| VAR { $$ = $VAR->value.var; }
| VAR "=" exp { $$ = $3; $VAR->value.var = $3; }
| FUN "(" exp ")" { $$ = $FUN->value.fun ($3); }
| exp[l] "+" exp[r] { $$ = $l + $r; }
| exp[l] "-" exp[r] { $$ = $l - $r; }
| exp[l] "*" exp[r] { $$ = $l * $r; }
| exp[l] "/" exp[r]
{
if ($r == 0)
{
yyerror (&@$, uctx, _("error: division by zero"));
YYERROR;
}
else
$$ = $l / $r;
}
| "-" exp %prec NEG { $$ = -$2; }
| exp[l] "^" exp[r] { $$ = pow ($l, $r); }
| "(" exp ")" { $$ = $2; }
| "(" error ")" { $$ = 666; }
;
// End of grammar.
%%
/*------------.
| Functions. |
`------------*/
struct init
{
char const *name;
func_t *fun;
};
static struct init const funs[] =
{
{ "atan", atan },
{ "cos", cos },
{ "exp", exp },
{ "ln", log },
{ "sin", sin },
{ "sqrt", sqrt },
{ 0, 0 },
};
// The symbol table: a chain of 'struct symrec'.
static symrec *sym_table;
// Put functions in table.
static void
init_table (void)
{
for (int i = 0; funs[i].name; i++)
{
symrec *ptr = putsym (funs[i].name, TOK_FUN);
ptr->value.fun = funs[i].fun;
}
}
symrec *
putsym (char const *name, int sym_type)
{
symrec *res = (symrec *) malloc (sizeof (symrec));
res->name = strdup (name);
res->type = sym_type;
res->value.var = 0; // Set value to 0 even if fun.
res->next = sym_table;
sym_table = res;
return res;
}
symrec *
getsym (char const *name)
{
for (symrec *p = sym_table; p; p = p->next)
if (strcmp (p->name, name) == 0)
return p;
return NULL;
}
// How many symbols are registered.
static int
symbol_count (void)
{
int res = 0;
for (symrec *p = sym_table; p; p = p->next)
++res;
return res;
}
/*------------.
| Locations. |
`------------*/
// Print *LOC on OUT. Do it in a compact way, that avoids redundancy.
static void
location_print (FILE *out, YYLTYPE const * const loc)
{
fprintf (out, "%d.%d", loc->first_line, loc->first_column);
int end_col = 0 != loc->last_column ? loc->last_column - 1 : 0;
if (loc->first_line < loc->last_line)
fprintf (out, "-%d.%d", loc->last_line, end_col);
else if (loc->first_column < end_col)
fprintf (out, "-%d", end_col);
}
/*----------.
| Scanner. |
`----------*/
yytoken_kind_t
yylex (const char **line, YYSTYPE *yylval, YYLTYPE *yylloc,
const user_context *uctx)
{
int c;
// Ignore white space, get first nonwhite character.
do {
// Move the first position onto the last.
yylloc->first_line = yylloc->last_line;
yylloc->first_column = yylloc->last_column;
yylloc->last_column += 1;
c = *((*line)++);
} while (c == ' ' || c == '\t');
switch (c)
{
case '+': return TOK_PLUS;
case '-': return TOK_MINUS;
case '*': return TOK_STAR;
case '/': return TOK_SLASH;
case '^': return TOK_CARET;
case '=': return TOK_EQUAL;
case '(': return TOK_LPAREN;
case ')': return TOK_RPAREN;
case '!': return TOK_YYUNDEF;
case '\0': return TOK_YYEOF;
// Numbers.
case '.':
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
{
int nchars = 0;
if (sscanf (*line - 1, "%lf%n", &yylval->TOK_NUM, &nchars) != 1)
abort ();
*line += nchars - 1;
yylloc->last_column += nchars - 1;
return TOK_NUM;
}
// Identifiers.
case 'a': case 'b': case 'c': case 'd': case 'e':
case 'f': case 'g': case 'h': case 'i': case 'j':
case 'k': case 'l': case 'm': case 'n': case 'o':
case 'p': case 'q': case 'r': case 's': case 't':
case 'u': case 'v': case 'w': case 'x': case 'y':
case 'z':
{
int nchars = 0;
char buf[100];
if (sscanf (*line - 1, "%99[a-z]%n", buf, &nchars) != 1)
abort ();
*line += nchars - 1;
yylloc->last_column += nchars - 1;
if (strcmp (buf, "exit") == 0)
return TOK_EXIT;
else
{
symrec *s = getsym (buf);
if (!s)
s = putsym (buf, TOK_VAR);
yylval->TOK_VAR = s;
return s->type;
}
}
// Stray characters.
default:
yyerror (yylloc, uctx, _("syntax error: invalid character: %c"), c);
return TOK_YYerror;
}
}
/*---------.
| Parser. |
`---------*/
static const char *
error_format_string (int argc)
{
switch (argc)
{
default: // Avoid compiler warnings.
case 0: return _("%@: syntax error");
case 1: return _("%@: syntax error: unexpected %u");
// TRANSLATORS: '%@' is a location in a file, '%u' is an
// "unexpected token", and '%0e', '%1e'... are expected tokens
// at this point.
//
// For instance on the expression "1 + * 2", you'd get
//
// 1.5: syntax error: expected - or ( or number or function or variable before *
case 2: return _("%@: syntax error: expected %0e before %u");
case 3: return _("%@: syntax error: expected %0e or %1e before %u");
case 4: return _("%@: syntax error: expected %0e or %1e or %2e before %u");
case 5: return _("%@: syntax error: expected %0e or %1e or %2e or %3e before %u");
case 6: return _("%@: syntax error: expected %0e or %1e or %2e or %3e or %4e before %u");
case 7: return _("%@: syntax error: expected %0e or %1e or %2e or %3e or %4e or %5e before %u");
case 8: return _("%@: syntax error: expected %0e or %1e or %2e or %3e or %4e or %5e etc., before %u");
}
}
int
yyreport_syntax_error (const yypcontext_t *ctx, const user_context *uctx)
{
if (uctx->silent)
return 0;
enum { ARGS_MAX = 6 };
yysymbol_kind_t arg[ARGS_MAX];
int argsize = yypcontext_expected_tokens (ctx, arg, ARGS_MAX);
if (argsize < 0)
return argsize;
const int too_many_expected_tokens = argsize == 0 && arg[0] != YYSYMBOL_YYEMPTY;
if (too_many_expected_tokens)
argsize = ARGS_MAX;
const char *format = error_format_string (1 + argsize + too_many_expected_tokens);
const YYLTYPE *loc = yypcontext_location (ctx);
while (*format)
// %@: location.
if (format[0] == '%' && format[1] == '@')
{
YYLOCATION_PRINT (stderr, loc);
format += 2;
}
// %u: unexpected token.
else if (format[0] == '%' && format[1] == 'u')
{
fputs (yysymbol_name (yypcontext_token (ctx)), stderr);
format += 2;
}
// %0e, %1e...: expected token.
else if (format[0] == '%'
&& isdigit ((unsigned char) format[1])
&& format[2] == 'e'
&& (format[1] - '0') < argsize)
{
int i = format[1] - '0';
fputs (yysymbol_name (arg[i]), stderr);
format += 3;
}
else
{
fputc (*format, stderr);
++format;
}
fputc ('\n', stderr);
// Quote the source line.
{
fprintf (stderr, "%5d | %s\n", loc->first_line, uctx->line);
fprintf (stderr, "%5s | %*s", "", loc->first_column, "^");
for (int i = loc->last_column - loc->first_column - 1; 0 < i; --i)
putc ('~', stderr);
putc ('\n', stderr);
}
return 0;
}
// Called by yyparse on errors to report the error to the user.
void
yyerror (const YYLTYPE *loc, const user_context *uctx, char const *format, ...)
{
if (uctx->silent)
return;
YYLOCATION_PRINT (stderr, loc);
fputs (": ", stderr);
va_list args;
va_start (args, format);
vfprintf (stderr, format, args);
va_end (args);
putc ('\n', stderr);
}
// Return a newly allocated copy of at most N bytes of STRING. In
// other words, return a copy of the initial segment of length N of
// STRING.
static char *
xstrndup (const char *string, size_t n)
{
// len = strnlen (string, n), portably.
const char *end = memchr (string, '\0', n);
size_t len = end ? (size_t) (end - string) : n;
char *new = malloc (len + 1);
if (!new)
abort ();
new[len] = '\0';
return memcpy (new, string, len);
}
/*-----------.
| Readline. |
`-----------*/
// Parse (and execute) this line.
static int
process_line (YYLTYPE *lloc, const char *line)
{
user_context uctx = {0, line};
yypstate *ps = yypstate_new ();
int status = 0;
do {
YYSTYPE lval;
yytoken_kind_t token = yylex (&line, &lval, lloc, &uctx);
status = yypush_parse (ps, token, &lval, lloc, &uctx);
} while (status == YYPUSH_MORE);
yypstate_delete (ps);
lloc->last_line++;
lloc->last_column = 1;
return status;
}
// Get the list of possible tokens after INPUT was read.
// Returns a nonnegative.
static int
expected_tokens (const char *input,
int *tokens, int ntokens)
{
YYDPRINTF ((stderr, "expected_tokens (\"%s\")", input));
user_context uctx = {1, input};
// Parse the current state of the line.
yypstate *ps = yypstate_new ();
int status = 0;
YYLTYPE lloc = { 1, 1, 1, 1 };
do {
YYSTYPE lval;
yytoken_kind_t token = yylex (&input, &lval, &lloc, &uctx);
// Don't let the parse know when we reach the end of input.
if (token == TOK_YYEOF)
break;
status = yypush_parse (ps, token, &lval, &lloc, &uctx);
} while (status == YYPUSH_MORE);
int res = 0;
// If there were parse errors, don't propose completions.
if (!ps->yynerrs)
{
// Then query for the accepted tokens at this point.
res = yypstate_expected_tokens (ps, tokens, ntokens);
if (res < 0)
abort ();
}
yypstate_delete (ps);
return res;
}
// Attempt to complete on the contents of TEXT. START and END bound
// the region of rl_line_buffer that contains the word to complete.
// TEXT is the word to complete. We can use the entire contents of
// rl_line_buffer in case we want to do some simple parsing. Return
// the array of matches, or NULL if there aren't any.
static char **
completion (const char *text, int start, int end)
{
YYDPRINTF ((stderr, "completion (\"%.*s[%.*s]%s\")\n",
start, rl_line_buffer,
end - start, rl_line_buffer + start,
rl_line_buffer + end));
// Get list of token numbers.
int tokens[YYNTOKENS];
char *line = xstrndup (rl_line_buffer, (size_t) start);
int ntokens = expected_tokens (line, tokens, YYNTOKENS);
free (line);
// Build MATCHES, the list of possible completions.
const size_t len = strlen (text);
// Need initial prefix and final NULL.
char **matches
= calloc ((size_t) ntokens + (size_t) symbol_count () + 2, sizeof *matches);
if (!matches)
abort ();
int match = 1;
for (int i = 0; i < ntokens; ++i)
switch (tokens[i])
{
case YYSYMBOL_FUN:
for (symrec *s = sym_table; s; s = s->next)
if (s->type == TOK_FUN && strncmp (text, s->name, len) == 0)
matches[match++] = strdup (s->name);
break;
case YYSYMBOL_VAR:
for (symrec *s = sym_table; s; s = s->next)
if (s->type == TOK_VAR && strncmp (text, s->name, len) == 0)
matches[match++] = strdup (s->name);
break;
default:
{
const char* token = yysymbol_name (tokens[i]);
if (strncmp (text, token, len) == 0)
matches[match++] = strdup (token);
break;
}
}
// Find the longest common prefix, and install it in matches[0], as
// required by readline.
if (match == 1)
matches[0] = strdup (text);
else
{
size_t lcplen = strlen (matches[1]);
for (int i = 2; i < match && lcplen; ++i)
for (size_t j = 0; j < lcplen; ++j)
if (matches[1][j] != matches[i][j])
lcplen = j;
matches[0] = xstrndup (matches[1], lcplen);
}
if (yydebug)
{
fprintf (stderr, "completion (\"%.*s[%.*s]%s\") = ",
start, rl_line_buffer,
end - start, rl_line_buffer + start,
rl_line_buffer + end);
for (int i = 1; matches[i]; ++i)
fprintf (stderr, "%s%s",
i == 1 ? "{" : ", ",
matches[i]);
fprintf (stderr, "}\n");
}
// Don't fall back to proposing file names.
rl_attempted_completion_over = 1;
return matches;
}
static void
init_readline (void)
{
// Allow conditional parsing of the ~/.inputrc file.
rl_readline_name = "bistromathic";
// Tell the completer that we want a crack first.
rl_attempted_completion_function = completion;
// The basic list of characters that signal a break between words
// for the completer routine.
rl_basic_word_break_characters = " \t\n\"\\'`@$><=;|&{(+-*/^)";
}
/*-------.
| Main. |
`-------*/
int
main (int argc, char const* argv[])
{
#if defined ENABLE_NLS && ENABLE_NLS
// Set up internationalization.
setlocale (LC_ALL, "");
// Use Bison's standard translation catalog for error messages
// (the generated messages).
bindtextdomain ("bison-runtime", BISON_LOCALEDIR);
// The translation catalog of bistromathic is actually included in
// Bison's. In your own project, use the name of your project.
bindtextdomain ("bison", LOCALEDIR);
textdomain ("bison");
#endif
// Enable parse traces on option -p.
if (1 < argc && strcmp (argv[1], "-p") == 0)
yydebug = 1;
init_table ();
init_readline ();
YYLTYPE lloc = {1, 1, 1, 1};
while (!done)
{
char *line = readline ("> ");
if (!line)
{
// Finish the line started by the prompt.
putchar ('\n');
break;
}
if (*line)
add_history (line);
process_line (&lloc, line);
free (line);
}
}
@@ -0,0 +1,23 @@
# This Makefile is designed to be simple and readable. It does not
# aim at portability. It requires GNU Make.
BASE = calc
BISON = bison
all: $(BASE)
%.c %.h %.html %.xml %.gv: %.y
$(BISON) $(BISONFLAGS) --header --html --graph -o $*.c $<
$(BASE): $(BASE).o
$(CC) $(CFLAGS) -o $@ $^
run: $(BASE)
@echo "Type arithmetic expressions. Quit with ctrl-d."
./$<
CLEANFILES = \
$(BASE) *.o $(BASE).[ch] $(BASE).output $(BASE).xml $(BASE).html $(BASE).gv
clean:
rm -f $(CLEANFILES)
@@ -0,0 +1,23 @@
# calc - calculator with Bison
This directory contains calc, the traditional example of using Bison to
build a simple calculator.
<!---
Local Variables:
fill-column: 76
ispell-dictionary: "american"
End:
Copyright (C) 2019-2021 Free Software Foundation, Inc.
This file is part of Bison, the GNU Compiler Compiler.
Permission is granted to copy, distribute and/or modify this document
under the terms of the GNU Free Documentation License, Version 1.3 or
any later version published by the Free Software Foundation; with no
Invariant Sections, with no Front-Cover Texts, and with no Back-Cover
Texts. A copy of the license is included in the "GNU Free
Documentation License" file as part of this distribution.
--->
@@ -0,0 +1,43 @@
#! /bin/sh
# Copyright (C) 2019-2021 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 <https://www.gnu.org/licenses/>.
cat >input <<EOF
1+2*3
EOF
run 0 7
cat >input <<EOF
1 - 2 - 3
EOF
run 0 -4
cat >input <<EOF
8 / 2 / 2
EOF
run 0 2
cat >input <<EOF
(1+2) * 3
EOF
run 0 9
# Check the traces.
run -noerr 0 9 -p
cat >input <<EOF
1++2
EOF
run 0 "err: syntax error, unexpected '+', expecting number or '('"
@@ -0,0 +1,102 @@
%code top {
#include <assert.h>
#include <ctype.h> /* isdigit. */
#include <stdio.h> /* printf. */
#include <stdlib.h> /* abort. */
#include <string.h> /* strcmp. */
int yylex (void);
void yyerror (char const *);
}
%define api.header.include {"calc.h"}
/* Generate YYSTYPE from the types used in %token and %type. */
%define api.value.type union
%token <double> NUM "number"
%type <double> expr term fact
/* Generate the parser description file (calc.output). */
%verbose
/* Nice error messages with details. */
%define parse.error detailed
/* Enable run-time traces (yydebug). */
%define parse.trace
/* Formatting semantic values in debug traces. */
%printer { fprintf (yyo, "%g", $$); } <double>;
%% /* The grammar follows. */
input:
%empty
| input line
;
line:
'\n'
| expr '\n' { printf ("%.10g\n", $1); }
| error '\n' { yyerrok; }
;
expr:
expr '+' term { $$ = $1 + $3; }
| expr '-' term { $$ = $1 - $3; }
| term
;
term:
term '*' fact { $$ = $1 * $3; }
| term '/' fact { $$ = $1 / $3; }
| fact
;
fact:
"number"
| '(' expr ')' { $$ = $2; }
;
%%
int
yylex (void)
{
int c;
/* Ignore white space, get first nonwhite character. */
while ((c = getchar ()) == ' ' || c == '\t')
continue;
if (c == EOF)
return 0;
/* Char starts a number => parse the number. */
if (c == '.' || isdigit (c))
{
ungetc (c, stdin);
if (scanf ("%lf", &yylval.NUM) != 1)
abort ();
return NUM;
}
/* Any other character is a token by itself. */
return c;
}
/* Called by yyparse on error. */
void
yyerror (char const *s)
{
fprintf (stderr, "%s\n", s);
}
int
main (int argc, char const* argv[])
{
/* Enable parse traces on option -p. */
for (int i = 1; i < argc; ++i)
if (!strcmp (argv[i], "-p"))
yydebug = 1;
return yyparse ();
}
@@ -0,0 +1,34 @@
## Copyright (C) 2019-2021 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 <https://www.gnu.org/licenses/>.
calcdir = $(docdir)/%D%
## ------ ##
## Calc. ##
## ------ ##
check_PROGRAMS += %D%/calc
TESTS += %D%/calc.test
EXTRA_DIST += %D%/calc.test
nodist_%C%_calc_SOURCES = %D%/calc.y
%D%/calc.c: $(dependencies)
# Don't use gnulib's system headers.
%C%_calc_CPPFLAGS = -I$(top_srcdir)/%D% -I$(top_builddir)/%D%
%C%_calc_CFLAGS = $(TEST_CFLAGS)
dist_calc_DATA = %D%/calc.y %D%/Makefile %D%/README.md
CLEANFILES += %D%/calc.[ch] %D%/calc.output %D%/scan.c
CLEANDIRS += %D%/*.dSYM
@@ -0,0 +1,23 @@
# This Makefile is designed to be simple and readable. It does not
# aim at portability. It requires GNU Make.
BASE = c++-types
BISON = bison
all: $(BASE)
%.c %.h %.xml %.gv: %.y
$(BISON) $(BISONFLAGS) --header --graph -o $*.c $<
$(BASE): $(BASE).o
$(CC) $(CFLAGS) -o $@ $^
run: $(BASE)
@echo "Type C++ declarations or expressions. Quit with ctrl-d."
./$<
CLEANFILES = \
$(BASE) *.o $(BASE).[ch] $(BASE).output $(BASE).xml $(BASE).html $(BASE).gv
clean:
rm -f $(CLEANFILES)
@@ -0,0 +1,24 @@
# glr
This example demonstrates the use of GLR parsers to handle (local)
ambiguities in the C++ language. See the node "Merging GLR Parses" in
Bison's documentation.
<!---
Local Variables:
fill-column: 76
ispell-dictionary: "american"
End:
Copyright (C) 2020-2021 Free Software Foundation, Inc.
This file is part of Bison, the GNU Compiler Compiler.
Permission is granted to copy, distribute and/or modify this document
under the terms of the GNU Free Documentation License, Version 1.3 or
any later version published by the Free Software Foundation; with no
Invariant Sections, with no Front-Cover Texts, and with no Back-Cover
Texts. A copy of the license is included in the "GNU Free
Documentation License" file as part of this distribution.
--->
@@ -0,0 +1,47 @@
#! /bin/sh
# Copyright (C) 2020-2021 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 <https://www.gnu.org/licenses/>.
cat >input <<EOF
z + q;
T x;
T x = y;
x = y;
T (x) + y;
T (x);
T (y) = z + q;
T (y y) = z + q;
z + q;
EOF
run 0 "\
1.0-5: +(z, q)
3.0-3: <declare>(T, x)
5.0-7: <init-declare>(T, x, y)
7.0-5: =(x, y)
9.0-9: +(<cast>(x, T), y)
11.0-5: <OR>(<declare>(T, x), <cast>(x, T))
13.0-13: <OR>(<init-declare>(T, y, +(z, q)), =(<cast>(y, T), +(z, q)))
15.0-15: <error>
17.0-5: +(z, q)
err: 15.5: syntax error, unexpected identifier, expecting '=' or '+' or ')'"
@@ -0,0 +1,340 @@
/* -*- C -*-
Copyright (C) 2020-2021 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 <https://www.gnu.org/licenses/>.
*/
/* Simplified C++ Type and Expression Grammar.
Written by Paul Hilfinger for Bison's test suite. */
%define api.pure
%header
%define api.header.include {"c++-types.h"}
%locations
%debug
/* Nice error messages with details. */
%define parse.error detailed
%code requires
{
union Node {
struct {
int isNterm;
int parents;
} nodeInfo;
struct {
int isNterm; /* 1 */
int parents;
char const *form;
union Node *children[3];
} nterm;
struct {
int isNterm; /* 0 */
int parents;
char *text;
} term;
};
typedef union Node Node;
}
%define api.value.type union
%code
{
#include <assert.h>
#include <ctype.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static Node *new_nterm (char const *, Node *, Node *, Node *);
static Node *new_term (char *);
static void free_node (Node *);
static char *node_to_string (const Node *);
static void node_print (FILE *, const Node *);
static Node *stmt_merge (YYSTYPE x0, YYSTYPE x1);
static void yyerror (YYLTYPE const * const loc, const char *msg);
static yytoken_kind_t yylex (YYSTYPE *lval, YYLTYPE *lloc);
}
%expect-rr 1
%token
TYPENAME "typename"
ID "identifier"
%right '='
%left '+'
%glr-parser
%type <Node *> stmt expr decl declarator TYPENAME ID
%destructor { free_node ($$); } <Node *>
%printer { node_print (yyo, $$); } <Node *>
%%
prog : %empty
| prog stmt {
YYLOCATION_PRINT (stdout, &@2);
fputs (": ", stdout);
node_print (stdout, $2);
putc ('\n', stdout);
fflush (stdout);
free_node ($2);
}
;
stmt : expr ';' %merge <stmt_merge> { $$ = $1; }
| decl %merge <stmt_merge>
| error ';' { $$ = new_nterm ("<error>", NULL, NULL, NULL); }
;
expr : ID
| TYPENAME '(' expr ')'
{ $$ = new_nterm ("<cast>(%s, %s)", $3, $1, NULL); }
| expr '+' expr { $$ = new_nterm ("+(%s, %s)", $1, $3, NULL); }
| expr '=' expr { $$ = new_nterm ("=(%s, %s)", $1, $3, NULL); }
;
decl : TYPENAME declarator ';'
{ $$ = new_nterm ("<declare>(%s, %s)", $1, $2, NULL); }
| TYPENAME declarator '=' expr ';'
{ $$ = new_nterm ("<init-declare>(%s, %s, %s)", $1,
$2, $4); }
;
declarator
: ID
| '(' declarator ')' { $$ = $2; }
;
%%
/* A C error reporting function. */
static void
yyerror (YYLTYPE const * const loc, const char *msg)
{
YYLOCATION_PRINT (stderr, loc);
fprintf (stderr, ": %s\n", msg);
}
/* The input file. */
FILE * input = NULL;
yytoken_kind_t
yylex (YYSTYPE *lval, YYLTYPE *lloc)
{
static int lineNum = 1;
static int colNum = 0;
while (1)
{
int c;
assert (!feof (input));
c = getc (input);
switch (c)
{
case EOF:
return 0;
case '\t':
colNum = (colNum + 7) & ~7;
break;
case ' ': case '\f':
colNum += 1;
break;
case '\n':
lineNum += 1;
colNum = 0;
break;
default:
{
yytoken_kind_t tok;
lloc->first_line = lloc->last_line = lineNum;
lloc->first_column = colNum;
if (isalpha (c))
{
char buffer[256];
unsigned i = 0;
do
{
buffer[i++] = (char) c;
colNum += 1;
assert (i != sizeof buffer - 1);
c = getc (input);
}
while (isalnum (c) || c == '_');
ungetc (c, input);
buffer[i++] = 0;
if (isupper ((unsigned char) buffer[0]))
{
tok = TYPENAME;
lval->TYPENAME = new_term (strcpy (malloc (i), buffer));
}
else
{
tok = ID;
lval->ID = new_term (strcpy (malloc (i), buffer));
}
}
else
{
colNum += 1;
tok = c;
}
lloc->last_column = colNum;
return tok;
}
}
}
}
static Node *
new_nterm (char const *form, Node *child0, Node *child1, Node *child2)
{
Node *res = malloc (sizeof *res);
res->nterm.isNterm = 1;
res->nterm.parents = 0;
res->nterm.form = form;
res->nterm.children[0] = child0;
if (child0)
child0->nodeInfo.parents += 1;
res->nterm.children[1] = child1;
if (child1)
child1->nodeInfo.parents += 1;
res->nterm.children[2] = child2;
if (child2)
child2->nodeInfo.parents += 1;
return res;
}
static Node *
new_term (char *text)
{
Node *res = malloc (sizeof *res);
res->term.isNterm = 0;
res->term.parents = 0;
res->term.text = text;
return res;
}
static void
free_node (Node *node)
{
if (!node)
return;
node->nodeInfo.parents -= 1;
/* Free only if 0 (last parent) or -1 (no parents). */
if (node->nodeInfo.parents > 0)
return;
if (node->nodeInfo.isNterm == 1)
{
free_node (node->nterm.children[0]);
free_node (node->nterm.children[1]);
free_node (node->nterm.children[2]);
}
else
free (node->term.text);
free (node);
}
static char *
node_to_string (const Node *node)
{
char *res;
if (!node)
{
res = malloc (1);
res[0] = 0;
}
else if (node->nodeInfo.isNterm == 1)
{
char *child0 = node_to_string (node->nterm.children[0]);
char *child1 = node_to_string (node->nterm.children[1]);
char *child2 = node_to_string (node->nterm.children[2]);
res = malloc (strlen (node->nterm.form) + strlen (child0)
+ strlen (child1) + strlen (child2) + 1);
sprintf (res, node->nterm.form, child0, child1, child2);
free (child2);
free (child1);
free (child0);
}
else
res = strdup (node->term.text);
return res;
}
static void
node_print (FILE *out, const Node *n)
{
char *str = node_to_string (n);
fputs (str, out);
free (str);
}
static Node *
stmt_merge (YYSTYPE x0, YYSTYPE x1)
{
return new_nterm ("<OR>(%s, %s)", x0.stmt, x1.stmt, NULL);
}
static int
process (const char *file)
{
int is_stdin = !file || strcmp (file, "-") == 0;
if (is_stdin)
input = stdin;
else
input = fopen (file, "r");
assert (input);
int status = yyparse ();
if (!is_stdin)
fclose (input);
return status;
}
int
main (int argc, char **argv)
{
if (getenv ("YYDEBUG"))
yydebug = 1;
int ran = 0;
for (int i = 1; i < argc; ++i)
// Enable parse traces on option -p.
if (strcmp (argv[i], "-p") == 0)
yydebug = 1;
else
{
int status = process (argv[i]);
ran = 1;
if (!status)
return status;
}
if (!ran)
{
int status = process (NULL);
if (!status)
return status;
}
return 0;
}
@@ -0,0 +1,34 @@
## Copyright (C) 2020-2021 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 <https://www.gnu.org/licenses/>.
glrdir = $(docdir)/%D%
## ----------- ##
## c++-types. ##
## ----------- ##
check_PROGRAMS += %D%/c++-types
TESTS += %D%/c++-types.test
EXTRA_DIST += %D%/c++-types.test
nodist_%C%_c___types_SOURCES = %D%/c++-types.y
%D%/c++-types.c: $(dependencies)
# Don't use gnulib's system headers.
%C%_c___types_CPPFLAGS = -I$(top_srcdir)/%D% -I$(top_builddir)/%D%
%C%_c___types_CFLAGS = $(TEST_CFLAGS)
dist_glr_DATA = %D%/c++-types.y %D%/Makefile %D%/README.md
CLEANFILES += %D%/c++-types.[ch] %D%/c++-types.output
CLEANDIRS += %D%/*.dSYM
@@ -0,0 +1,29 @@
# This Makefile is designed to be simple and readable. It does not
# aim at portability. It requires GNU Make.
BASE = lexcalc
BISON = bison
FLEX = flex
all: $(BASE)
%.c %.h %.html %.xml %.gv: %.y
$(BISON) $(BISONFLAGS) --header --html --graph -o $*.c $<
%.c: %.l
$(FLEX) $(FLEXFLAGS) -o$@ $<
scan.o: parse.h
$(BASE): parse.o scan.o
$(CC) $(CFLAGS) -o $@ $^
run: $(BASE)
@echo "Type arithmetic expressions. Quit with ctrl-d."
./$<
CLEANFILES = \
$(BASE) *.o \
parse.[ch] parse.output parse.xml parse.html parse.gv \
scan.c
clean:
rm -f $(CLEANFILES)
@@ -0,0 +1,24 @@
# lexcalc - calculator with Flex and Bison
This directory contains lexcalc, the traditional example of using Flex and
Bison to build a simple calculator.
It features detailed syntax errors with locations.
<!---
Local Variables:
fill-column: 76
ispell-dictionary: "american"
End:
Copyright (C) 2018-2021 Free Software Foundation, Inc.
This file is part of Bison, the GNU Compiler Compiler.
Permission is granted to copy, distribute and/or modify this document
under the terms of the GNU Free Documentation License, Version 1.3 or
any later version published by the Free Software Foundation; with no
Invariant Sections, with no Front-Cover Texts, and with no Back-Cover
Texts. A copy of the license is included in the "GNU Free
Documentation License" file as part of this distribution.
--->
@@ -0,0 +1,46 @@
#! /bin/sh
# Copyright (C) 2018-2021 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 <https://www.gnu.org/licenses/>.
cat >input <<EOF
1+2*3
EOF
run 0 7
cat >input <<EOF
1
2
3
EOF
run 0 '1
2
3'
cat >input <<EOF
(1+2) * 3
EOF
run 0 9
run -noerr 0 9 -p
cat >input <<EOF
(1+2) *
EOF
run 1 'err: 1.8-2.0: syntax error, unexpected end of line, expecting ( or number'
cat >input <<EOF
1 / (2 - 2)
EOF
run 1 'err: 1.1-11: error: division by zero'
@@ -0,0 +1,44 @@
## Copyright (C) 2018-2021 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 <https://www.gnu.org/licenses/>.
lexcalcdir = $(docdir)/%D%
## --------- ##
## LexCalc. ##
## --------- ##
if FLEX_WORKS
check_PROGRAMS += %D%/lexcalc
TESTS += %D%/lexcalc.test
nodist_%C%_lexcalc_SOURCES = %D%/parse.y %D%/parse.h %D%/scan.l
# Don't use gnulib's system headers.
%C%_lexcalc_CPPFLAGS = -I$(top_srcdir)/%D% -I$(top_builddir)/%D%
# Fighting warnings triggered by Flex is just too painful.
# %C%_lexcalc_CFLAGS = $(TEST_CFLAGS)
endif FLEX_WORKS
%D%/parse.c: $(dependencies)
# Tell Make scan.o depends on parse.h, except that Make sees only
# parse.c, not parse.h. We can't use BUILT_SOURCES to this end, since
# we use the built bison.
%D%/lexcalc$(DASH)scan.o: %D%/parse.c
# Likewise, but for Automake before 1.16.
%D%/examples_c_lexcalc_lexcalc$(DASH)scan.o: %D%/parse.c
EXTRA_DIST += %D%/lexcalc.test
dist_lexcalc_DATA = %D%/parse.y %D%/scan.l %D%/Makefile %D%/README.md
CLEANFILES += %D%/parse.[ch] %D%/scan.c %D%/parse.output
CLEANDIRS += %D%/*.dSYM
@@ -0,0 +1,138 @@
/* Parser for lexcalc. -*- C -*-
Copyright (C) 2018-2021 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 <https://www.gnu.org/licenses/>. */
// Prologue (directives).
%expect 0
// Emitted in the header file, after the definition of YYSTYPE.
%code provides
{
// Tell Flex the expected prototype of yylex.
#define YY_DECL \
yytoken_kind_t yylex (YYSTYPE* yylval, YYLTYPE *yylloc, int *nerrs)
YY_DECL;
void yyerror (const YYLTYPE *loc, int *nerrs, const char *msg);
}
// Emitted on top of the implementation file.
%code top
{
#include <stdio.h> // printf.
#include <stdlib.h> // getenv.
#include <string.h> // strcmp.
}
// Include the header in the implementation rather than duplicating it.
%define api.header.include {"parse.h"}
// Don't share global variables between the scanner and the parser.
%define api.pure full
// To avoid name clashes (e.g., with C's EOF) prefix token definitions
// with TOK_ (e.g., TOK_EOF).
%define api.token.prefix {TOK_}
// %token and %type use genuine types (e.g., "%token <int>"). Let
// %bison define YYSTYPE as a union of all these types.
%define api.value.type union
// Generate detailed error messages.
%define parse.error detailed
// with locations.
%locations
// Enable debug traces (see yydebug in main).
%define parse.trace
// Error count, exchanged between main, yyparse and yylex.
%param {int *nerrs}
%token
PLUS "+"
MINUS "-"
STAR "*"
SLASH "/"
LPAREN "("
RPAREN ")"
EOL "end of line"
;
%token <int> NUM "number"
%type <int> exp
%printer { fprintf (yyo, "%d", $$); } <int>
// Precedence (from lowest to highest) and associativity.
%left "+" "-"
%left "*" "/"
%%
// Rules.
input:
%empty
| input line
;
line:
exp EOL { printf ("%d\n", $exp); }
| error EOL { yyerrok; }
;
exp:
exp "+" exp { $$ = $1 + $3; }
| exp "-" exp { $$ = $1 - $3; }
| exp "*" exp { $$ = $1 * $3; }
| exp "/" exp
{
if ($3 == 0)
{
yyerror (&@$, nerrs, "error: division by zero");
YYERROR;
}
else
$$ = $1 / $3;
}
| "(" exp ")" { $$ = $2; }
| NUM { $$ = $1; }
;
%%
// Epilogue (C code).
void yyerror (const YYLTYPE *loc, int *nerrs, const char *msg)
{
YYLOCATION_PRINT (stderr, loc);
fprintf (stderr, ": %s\n", msg);
++*nerrs;
}
int main (int argc, const char *argv[])
{
// Possibly enable parser runtime debugging.
yydebug = !!getenv ("YYDEBUG");
// Enable parse traces on option -p.
for (int i = 1; i < argc; ++i)
if (strcmp (argv[i], "-p") == 0)
yydebug = 1;
int nerrs = 0;
yyparse (&nerrs);
// Exit on failure if there were errors.
return !!nerrs;
}
@@ -0,0 +1,78 @@
/* Scanner for lexcalc. -*- C -*-
Copyright (C) 2018-2021 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 <https://www.gnu.org/licenses/>. */
/* Prologue (directives). */
/* Disable Flex features we don't need, to avoid warnings. */
%option nodefault noinput nounput noyywrap
%{
#include <errno.h> /* errno, ERANGE */
#include <limits.h> /* INT_MIN */
#include <stdlib.h> /* strtol */
#include "parse.h"
// Each time a rule is matched, advance the end cursor/position.
#define YY_USER_ACTION \
yylloc->last_column += (int) yyleng;
// Move the first position onto the last.
#define LOCATION_STEP() \
do { \
yylloc->first_line = yylloc->last_line; \
yylloc->first_column = yylloc->last_column; \
} while (0)
%}
%%
%{
// Each time yylex is called, move the head position to the end one.
LOCATION_STEP ();
%}
/* Rules. */
"+" return TOK_PLUS;
"-" return TOK_MINUS;
"*" return TOK_STAR;
"/" return TOK_SLASH;
"(" return TOK_LPAREN;
")" return TOK_RPAREN;
/* Scan an integer. */
[0-9]+ {
errno = 0;
long n = strtol (yytext, NULL, 10);
if (! (INT_MIN <= n && n <= INT_MAX && errno != ERANGE))
yyerror (yylloc, nerrs, "integer is out of range");
yylval->TOK_NUM = (int) n;
return TOK_NUM;
}
"\n" yylloc->last_line++; yylloc->last_column = 1; return TOK_EOL;
/* Ignore white spaces. */
[ \t]+ LOCATION_STEP (); continue;
. yyerror (yylloc, nerrs, "syntax error, invalid character"); continue;
<<EOF>> return TOK_YYEOF;
%%
/* Epilogue (C code). */
@@ -0,0 +1,26 @@
## Copyright (C) 2018-2021 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 <https://www.gnu.org/licenses/>.
cdir = $(docdir)/%D%
dist_c_DATA = %D%/README.md
include %D%/bistromathic/local.mk
include %D%/calc/local.mk
include %D%/glr/local.mk
include %D%/lexcalc/local.mk
include %D%/mfcalc/local.mk
include %D%/pushcalc/local.mk
include %D%/reccalc/local.mk
include %D%/rpcalc/local.mk
@@ -0,0 +1,20 @@
# This Makefile is designed to be simple and readable. It does not
# aim at portability. It requires GNU Make.
BASE = mfcalc
BISON = bison
all: $(BASE)
%.c %.html %.gv: %.y
$(BISON) $(BISONFLAGS) --html --graph -o $*.c $<
%: %.c
$(CC) $(CFLAGS) -o $@ $<
run: $(BASE)
@echo "Type arithmetic expressions. Quit with ctrl-d."
./$<
clean:
rm -f $(BASE) $(BASE).c $(BASE).html $(BASE).xml $(BASE).gv $(BASE).output
@@ -0,0 +1,43 @@
/* Functions for mfcalc. -*- C -*-
Copyright (C) 1988-1993, 1995, 1998-2015, 2018-2021 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 <https://www.gnu.org/licenses/>. */
/* Function type. */
typedef double (func_t) (double);
/* Data type for links in the chain of symbols. */
struct symrec
{
char *name; /* name of symbol */
int type; /* type of symbol: either VAR or FUN */
union
{
double var; /* value of a VAR */
func_t *fun; /* value of a FUN */
} value;
struct symrec *next; /* link field */
};
typedef struct symrec symrec;
/* The symbol table: a chain of 'struct symrec'. */
extern symrec *sym_table;
symrec *putsym (char const *name, int sym_type);
symrec *getsym (char const *name);
@@ -0,0 +1,45 @@
## Copyright (C) 2005-2006, 2008-2015, 2018-2021 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 <https://www.gnu.org/licenses/>.
## -------------------- ##
## Building & testing. ##
## -------------------- ##
BUILT_SOURCES += $(mfcalc_sources)
CLEANFILES += %D%/mfcalc.[ch] %D%/mfcalc.output
CLEANDIRS += %D%/*.dSYM
mfcalc_extracted = %D%/calc.h %D%/mfcalc.y
mfcalc_sources = $(mfcalc_extracted)
extracted += $(mfcalc_extracted)
check_PROGRAMS += %D%/mfcalc
nodist_%C%_mfcalc_SOURCES = $(mfcalc_sources)
%D%/mfcalc.c: $(dependencies)
# Don't use gnulib's system headers.
%C%_mfcalc_CPPFLAGS = -I$(top_srcdir)/%D% -I$(top_builddir)/%D%
%C%_mfcalc_CFLAGS = $(TEST_CFLAGS)
%C%_mfcalc_LDADD = -lm
dist_TESTS += %D%/mfcalc.test
## ------------ ##
## Installing. ##
## ------------ ##
mfcalcdir = $(docdir)/%D%
mfcalc_DATA = $(mfcalc_extracted)
dist_mfcalc_DATA = %D%/Makefile
@@ -0,0 +1,39 @@
#! /bin/sh
# Copyright (C) 2005-2015, 2018-2021 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 <https://www.gnu.org/licenses/>.
cat >input <<EOF
1+2*3
EOF
run 0 7
cat >input <<EOF
(1+2) * 3
EOF
run 0 9
run -noerr 0 9 -p
cat >input <<EOF
a = 256
sqrt (a)
EOF
run 0 '256
16'
cat >input <<EOF
16 == 12
EOF
run 0 'err: syntax error'
@@ -0,0 +1,208 @@
/* Parser for mfcalc. -*- C -*-
Copyright (C) 1988-1993, 1995, 1998-2015, 2018-2021 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 <https://www.gnu.org/licenses/>. */
%{
#include <stdio.h> /* For printf, etc. */
#include <math.h> /* For pow, used in the grammar. */
#include "calc.h" /* Contains definition of 'symrec'. */
int yylex (void);
void yyerror (char const *);
%}
%define api.value.type union /* Generate YYSTYPE from these types: */
%token <double> NUM /* Double precision number. */
%token <symrec*> VAR FUN /* Symbol table pointer: variable/function. */
%nterm <double> exp
%precedence '='
%left '-' '+'
%left '*' '/'
%precedence NEG /* negation--unary minus */
%right '^' /* exponentiation */
/* Generate the parser description file. */
%verbose
/* Enable run-time traces (yydebug). */
%define parse.trace
/* Formatting semantic values. */
%printer { fprintf (yyo, "%s", $$->name); } VAR;
%printer { fprintf (yyo, "%s()", $$->name); } FUN;
%printer { fprintf (yyo, "%g", $$); } <double>;
%% /* The grammar follows. */
input:
%empty
| input line
;
line:
'\n'
| exp '\n' { printf ("%.10g\n", $1); }
| error '\n' { yyerrok; }
;
exp:
NUM
| VAR { $$ = $1->value.var; }
| VAR '=' exp { $$ = $3; $1->value.var = $3; }
| FUN '(' exp ')' { $$ = $1->value.fun ($3); }
| exp '+' exp { $$ = $1 + $3; }
| exp '-' exp { $$ = $1 - $3; }
| exp '*' exp { $$ = $1 * $3; }
| exp '/' exp { $$ = $1 / $3; }
| '-' exp %prec NEG { $$ = -$2; }
| exp '^' exp { $$ = pow ($1, $3); }
| '(' exp ')' { $$ = $2; }
;
/* End of grammar. */
%%
struct init
{
char const *name;
func_t *fun;
};
struct init const funs[] =
{
{ "atan", atan },
{ "cos", cos },
{ "exp", exp },
{ "ln", log },
{ "sin", sin },
{ "sqrt", sqrt },
{ 0, 0 },
};
/* The symbol table: a chain of 'struct symrec'. */
symrec *sym_table;
/* Put functions in table. */
static void
init_table (void)
{
for (int i = 0; funs[i].name; i++)
{
symrec *ptr = putsym (funs[i].name, FUN);
ptr->value.fun = funs[i].fun;
}
}
/* The mfcalc code assumes that malloc and realloc
always succeed, and that integer calculations
never overflow. Production-quality code should
not make these assumptions. */
#include <assert.h>
#include <stdlib.h> /* malloc, realloc. */
#include <string.h> /* strlen. */
symrec *
putsym (char const *name, int sym_type)
{
symrec *res = (symrec *) malloc (sizeof (symrec));
res->name = strdup (name);
res->type = sym_type;
res->value.var = 0; /* Set value to 0 even if fun. */
res->next = sym_table;
sym_table = res;
return res;
}
symrec *
getsym (char const *name)
{
for (symrec *p = sym_table; p; p = p->next)
if (strcmp (p->name, name) == 0)
return p;
return NULL;
}
#include <ctype.h>
#include <stddef.h>
int
yylex (void)
{
int c = getchar ();
/* Ignore white space, get first nonwhite character. */
while (c == ' ' || c == '\t')
c = getchar ();
if (c == EOF)
return YYEOF;
/* Char starts a number => parse the number. */
if (c == '.' || isdigit (c))
{
ungetc (c, stdin);
if (scanf ("%lf", &yylval.NUM) != 1)
abort ();
return NUM;
}
/* Char starts an identifier => read the name. */
if (isalpha (c))
{
static ptrdiff_t bufsize = 0;
static char *symbuf = 0;
ptrdiff_t i = 0;
do
{
/* If buffer is full, make it bigger. */
if (bufsize <= i)
{
bufsize = 2 * bufsize + 40;
symbuf = realloc (symbuf, (size_t) bufsize);
}
/* Add this character to the buffer. */
symbuf[i++] = (char) c;
/* Get another character. */
c = getchar ();
}
while (isalnum (c));
ungetc (c, stdin);
symbuf[i] = '\0';
symrec *s = getsym (symbuf);
if (!s)
s = putsym (symbuf, VAR);
yylval.VAR = s; /* or yylval.FUN = s. */
return s->type;
}
/* Any other character is a token by itself. */
return c;
}
/* Called by yyparse on error. */
void yyerror (char const *s)
{
fprintf (stderr, "%s\n", s);
}
int main (int argc, char const* argv[])
{
/* Enable parse traces on option -p. */
if (argc == 2 && strcmp(argv[1], "-p") == 0)
yydebug = 1;
init_table ();
return yyparse ();
}
@@ -0,0 +1,23 @@
# This Makefile is designed to be simple and readable. It does not
# aim at portability. It requires GNU Make.
BASE = calc
BISON = bison
all: $(BASE)
%.c %.h %.html %.gv: %.y
$(BISON) $(BISONFLAGS) --header --html --graph -o $*.c $<
$(BASE): $(BASE).o
$(CC) $(CFLAGS) -o $@ $^
run: $(BASE)
@echo "Type arithmetic expressions. Quit with ctrl-d."
./$<
CLEANFILES = \
$(BASE) *.o $(BASE).[ch] $(BASE).output $(BASE).xml $(BASE).html $(BASE).gv
clean:
rm -f $(CLEANFILES)
@@ -0,0 +1,32 @@
# pushcalc - push parser with Bison
This directory contains pushcalc, the traditional calculator, implemented as
a push parser.
Traditionally Bison is used to create so called "pull parsers": the user
invokes the parser once, which repeatedly calls (pulls) the scanner until
the input is drained.
This example demonstrates the "push parsers": the user calls scanner to
fetch the next token, passes (pushes) it to the parser, and repeats the
operation until the input is drained.
This example is a straightforward conversion of the 'calc' example to the
push-parser model.
<!---
Local Variables:
fill-column: 76
ispell-dictionary: "american"
End:
Copyright (C) 2020-2021 Free Software Foundation, Inc.
Permission is granted to copy, distribute and/or modify this document
under the terms of the GNU Free Documentation License, Version 1.3 or
any later version published by the Free Software Foundation; with no
Invariant Sections, with no Front-Cover Texts, and with no Back-Cover
Texts. A copy of the license is included in the "GNU Free
Documentation License" file as part of this distribution.
--->
@@ -0,0 +1,42 @@
#! /bin/sh
# Copyright (C) 2020-2021 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 <https://www.gnu.org/licenses/>.
cat >input <<EOF
1+2*3
EOF
run 0 7
cat >input <<EOF
1 - 2 - 3
EOF
run 0 -4
cat >input <<EOF
8 / 2 / 2
EOF
run 0 2
cat >input <<EOF
(1+2) * 3
EOF
run 0 9
run -noerr 0 9 -p
cat >input <<EOF
1++2
EOF
run 0 "err: syntax error, unexpected '+', expecting number or '('"
@@ -0,0 +1,134 @@
/* Parser and scanner for pushcalc. -*- C -*-
Copyright (C) 2020-2021 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 <https://www.gnu.org/licenses/>. */
%code top {
#include <ctype.h> /* isdigit. */
#include <stdio.h> /* printf. */
#include <stdlib.h> /* abort. */
#include <string.h> /* strcmp. */
}
%code {
int yylex (YYSTYPE *yylval);
void yyerror (char const *);
}
%define api.header.include {"calc.h"}
/* Generate YYSTYPE from the types used in %token and %type. */
%define api.value.type union
%token <double> NUM "number"
%type <double> expr term fact
/* Don't share global variables between the scanner and the parser. */
%define api.pure full
/* Generate a push parser. */
%define api.push-pull push
/* Nice error messages with details. */
%define parse.error detailed
/* Generate the parser description file (calc.output). */
%verbose
/* Enable run-time traces (yydebug). */
%define parse.trace
/* Formatting semantic values in debug traces. */
%printer { fprintf (yyo, "%g", $$); } <double>;
%% /* The grammar follows. */
input:
%empty
| input line
;
line:
'\n'
| expr '\n' { printf ("%.10g\n", $1); }
| error '\n' { yyerrok; }
;
expr:
expr '+' term { $$ = $1 + $3; }
| expr '-' term { $$ = $1 - $3; }
| term
;
term:
term '*' fact { $$ = $1 * $3; }
| term '/' fact { $$ = $1 / $3; }
| fact
;
fact:
"number"
| '(' expr ')' { $$ = $expr; }
;
%%
int
yylex (YYSTYPE *yylval)
{
int c;
/* Ignore white space, get first nonwhite character. */
while ((c = getchar ()) == ' ' || c == '\t')
continue;
if (c == EOF)
return 0;
/* Char starts a number => parse the number. */
if (c == '.' || isdigit (c))
{
ungetc (c, stdin);
if (scanf ("%lf", &yylval->NUM) != 1)
abort ();
return NUM;
}
/* Any other character is a token by itself. */
return c;
}
/* Called by yyparse on error. */
void
yyerror (char const *s)
{
fprintf (stderr, "%s\n", s);
}
int
main (int argc, char const* argv[])
{
/* Enable parse traces on option -p. */
for (int i = 1; i < argc; ++i)
if (!strcmp (argv[i], "-p"))
yydebug = 1;
int status;
yypstate *ps = yypstate_new ();
do {
YYSTYPE lval;
status = yypush_parse (ps, yylex (&lval), &lval);
} while (status == YYPUSH_MORE);
yypstate_delete (ps);
return status;
}
@@ -0,0 +1,34 @@
## Copyright (C) 2020-2021 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 <https://www.gnu.org/licenses/>.
pushcalcdir = $(docdir)/%D%
## ------ ##
## Calc. ##
## ------ ##
check_PROGRAMS += %D%/calc
TESTS += %D%/calc.test
EXTRA_DIST += %D%/calc.test
nodist_%C%_calc_SOURCES = %D%/calc.y
%D%/calc.c: $(dependencies)
# Don't use gnulib's system headers.
%C%_calc_CPPFLAGS = -I$(top_srcdir)/%D% -I$(top_builddir)/%D%
%C%_calc_CFLAGS = $(TEST_CFLAGS)
dist_pushcalc_DATA = %D%/calc.y %D%/Makefile %D%/README.md
CLEANFILES += %D%/calc.[ch] %D%/calc.output
CLEANDIRS += %D%/*.dSYM
@@ -0,0 +1,30 @@
# This Makefile is designed to be simple and readable. It does not
# aim at portability. It requires GNU Make.
BASE = reccalc
BISON = bison
FLEX = flex
all: $(BASE)
%.c %.h %.xml %.gv: %.y
$(BISON) $(BISONFLAGS) --header --graph -o $*.c $<
%.c %.h: %.l
$(FLEX) $(FLEXFLAGS) -o$*.c --header=$*.h $<
scan.o: parse.h
parse.o: scan.h
$(BASE): parse.o scan.o
$(CC) $(CFLAGS) -o $@ $^
run: $(BASE)
@echo "Type arithmetic expressions. Quit with ctrl-d."
./$<
CLEANFILES = \
$(BASE) *.o \
parse.[ch] parse.output parse.xml parse.html parse.gv \
scan.[ch]
clean:
rm -f $(CLEANFILES)
@@ -0,0 +1,38 @@
# reccalc - recursive calculator with Flex and Bison
In this example the generated parser is pure and reentrant: it can be used
concurrently in different threads, or recursively. As a proof of this
reentrancy, expressions in parenthesis are tokenized as strings, and then
recursively parsed from the parser:
```
exp: STR
{
result r = parse_string ($1);
free ($1);
if (r.nerrs)
{
res->nerrs += r.nerrs;
YYERROR;
}
else
$$ = r.value;
}
```
<!---
Local Variables:
fill-column: 76
ispell-dictionary: "american"
End:
Copyright (C) 2018-2021 Free Software Foundation, Inc.
Permission is granted to copy, distribute and/or modify this document
under the terms of the GNU Free Documentation License, Version 1.3 or
any later version published by the Free Software Foundation; with no
Invariant Sections, with no Front-Cover Texts, and with no Back-Cover
Texts. A copy of the license is included in the "GNU Free
Documentation License" file as part of this distribution.
--->
@@ -0,0 +1,83 @@
## Copyright (C) 2019-2021 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 <https://www.gnu.org/licenses/>.
reccalcdir = $(docdir)/%D%
## --------- ##
## RecCalc. ##
## --------- ##
if FLEX_WORKS
check_PROGRAMS += %D%/reccalc
TESTS += %D%/reccalc.test
nodist_%C%_reccalc_SOURCES = %D%/parse.y %D%/scan.h %D%/scan.c
BUILT_SOURCES += $(nodist_%C%_reccalc_SOURCES)
# Don't use gnulib's system headers.
%C%_reccalc_CPPFLAGS = -I$(top_srcdir)/%D% -I$(top_builddir)/%D%
# Fighting warnings triggered by Flex is just too painful.
# %C%_reccalc_CFLAGS = $(TEST_CFLAGS)
endif FLEX_WORKS
%D%/parse.c: $(dependencies)
# Tell Make that parse.o depends on scan.h, so that scan.h is built
# before parse.o. Obfuscate the name of the target, otherwise
# Automake removes its recipe for parse.o and leaves only our
# additional dependency.
DASH = -
%D%/reccalc$(DASH)parse.o: %D%/scan.h
# Tell Make scan.o depends on parse.h, except that Make sees only
# parse.c, not parse.h. We can't use BUILT_SOURCES to this end, since
# we use the built bison.
%D%/reccalc$(DASH)scan.o: %D%/parse.c
# Likewise, but for Automake before 1.16.
%D%/examples_c_reccalc_reccalc$(DASH)parse.o: %D%/scan.h
%D%/examples_c_reccalc_reccalc$(DASH)scan.o: %D%/parse.c
## See "info automake 'Multiple Outputs'" for this rule.
%D%/scan.c %D%/scan.h: %D%/scan.stamp
## Recover from the removal of $@
@if test -f $@; then :; else \
trap 'rm -rf %D%/scan.lock %D%/scan.stamp' 1 2 13 15; \
## mkdir is a portable test-and-set
if mkdir %D%/scan.lock 2>/dev/null; then \
## This code is being executed by the first process.
rm -f %D%/scan.stamp; \
$(MAKE) $(AM_MAKEFLAGS) %D%/scan.stamp; \
result=$$?; rm -rf %D%/scan.lock; exit $$result; \
else \
## This code is being executed by the follower processes.
## Wait until the first process is done.
while test -d %D%/scan.lock; do sleep 1; done; \
## Succeed if and only if the first process succeeded.
test -f %D%/scan.stamp; \
fi; \
fi
%D%/scan.stamp: %D%/scan.l
$(AM_V_LEX)rm -f $@ $@.tmp
$(AM_V_at)$(MKDIR_P) %D%
$(AM_V_at)touch $@.tmp
## --header introduced in 2.5.6, renamed as --header-file in 2.6.4.
## Backward compatibility ensured since --header is an unambiguous prefix.
$(AM_V_at)$(LEX) $(AM_LFLAGS) $(LFLAGS) -o%D%/scan.c --header=%D%/scan.h $(srcdir)/%D%/scan.l
$(AM_V_at)mv $@.tmp $@
EXTRA_DIST += %D%/reccalc.test %D%/scan.l
dist_reccalc_DATA = %D%/parse.y %D%/scan.l %D%/Makefile %D%/README.md
CLEANFILES += %D%/parse.[ch] %D%/parse.output %D%/scan.[ch] %D%/*.stamp
CLEANDIRS += %D%/*.dSYM
@@ -0,0 +1,220 @@
/* Parser for reccalc. -*- C -*-
Copyright (C) 2019-2021 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 <https://www.gnu.org/licenses/>. */
// Prologue (directives).
%expect 0
// Emitted in the header file, before the definition of YYSTYPE.
%code requires
{
#ifndef YY_TYPEDEF_YY_SCANNER_T
# define YY_TYPEDEF_YY_SCANNER_T
typedef void* yyscan_t;
#endif
typedef struct
{
// Whether to print the intermediate results.
int verbose;
// Value of the last computation.
int value;
// Number of errors.
int nerrs;
} result;
}
// Emitted in the header file, after the definition of YYSTYPE.
%code provides
{
// Tell Flex the expected prototype of yylex.
// The scanner argument must be named yyscanner.
#define YY_DECL \
yytoken_kind_t yylex (YYSTYPE* yylval, yyscan_t yyscanner, result *res)
YY_DECL;
void yyerror (yyscan_t scanner, result *res, const char *msg, ...);
}
// Emitted on top of the implementation file.
%code top
{
#include <stdarg.h> // va_list.
#include <stdio.h> // printf.
#include <stdlib.h> // getenv.
}
%code
{
result parse_string (const char* cp);
result parse (void);
}
// Include the header in the implementation rather than duplicating it.
%define api.header.include {"parse.h"}
// Don't share global variables between the scanner and the parser.
%define api.pure full
// To avoid name clashes (e.g., with C's EOF) prefix token definitions
// with TOK_ (e.g., TOK_EOF).
%define api.token.prefix {TOK_}
// Generate YYSTYPE from the types assigned to symbols.
%define api.value.type union
// Error messages with "unexpected XXX, expected XXX...".
%define parse.error detailed
// Enable run-time traces (yydebug).
%define parse.trace
// Generate the parser description file (parse.output).
%verbose
// Scanner and error count are exchanged between main, yyparse and yylex.
%param {yyscan_t scanner}{result *res}
%token
PLUS "+"
MINUS "-"
STAR "*"
SLASH "/"
EOL "end-of-line"
EOF 0 "end-of-file"
;
%token <int> NUM "number"
%type <int> exp
%printer { fprintf (yyo, "%d", $$); } <int>
%token <char*> STR "string"
%printer { fprintf (yyo, "\"%s\"", $$); } <char*>
%destructor { free ($$); } <char*>
// Precedence (from lowest to highest) and associativity.
%left "+" "-"
%left "*" "/"
%precedence UNARY
%%
// Rules.
input:
line
| input line
;
line:
exp eol
{
res->value = $exp;
if (res->verbose)
printf ("%d\n", $exp);
}
| error eol
{
yyerrok;
}
;
eol:
EOF
| EOL
;
exp:
NUM { $$ = $1; }
| exp "+" exp { $$ = $1 + $3; }
| exp "-" exp { $$ = $1 - $3; }
| exp "*" exp { $$ = $1 * $3; }
| exp "/" exp
{
if ($3 == 0)
{
yyerror (scanner, res, "invalid division by zero");
YYERROR;
}
else
$$ = $1 / $3;
}
| "+" exp %prec UNARY { $$ = + $2; }
| "-" exp %prec UNARY { $$ = - $2; }
| STR
{
result r = parse_string ($1);
free ($1);
if (r.nerrs)
{
res->nerrs += r.nerrs;
YYERROR;
}
else
$$ = r.value;
}
;
%%
// Epilogue (C code).
#include "scan.h"
result
parse (void)
{
yyscan_t scanner;
yylex_init (&scanner);
result res = {1, 0, 0};
yyparse (scanner, &res);
yylex_destroy (scanner);
return res;
}
result
parse_string (const char *str)
{
yyscan_t scanner;
yylex_init (&scanner);
YY_BUFFER_STATE buf = yy_scan_string (str ? str : "", scanner);
result res = {0, 0, 0};
yyparse (scanner, &res);
yy_delete_buffer (buf, scanner);
yylex_destroy (scanner);
return res;
}
void
yyerror (yyscan_t scanner, result *res,
const char *msg, ...)
{
(void) scanner;
va_list args;
va_start (args, msg);
vfprintf (stderr, msg, args);
va_end (args);
fputc ('\n', stderr);
res->nerrs += 1;
}
int
main (void)
{
// Possibly enable parser runtime debugging.
yydebug = !!getenv ("YYDEBUG");
result res = parse ();
// Exit on failure if there were errors.
return !!res.nerrs;
}
@@ -0,0 +1,59 @@
#! /bin/sh
# Copyright (C) 2018-2021 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 <https://www.gnu.org/licenses/>.
(seq 0) >/dev/null 2>&1 || skip "gimme one seq"
cat >input <<EOF
1+2*3
EOF
run 0 '7'
cat >input <<EOF
(1+2) * 3
EOF
run 0 '9'
run -noerr 0 '9' -p
cat >input <<EOF
(((1)+(2))*((3)+(4)))
EOF
run 0 '21'
# Some really deep computation.
# for 4, gives 4 + (3 + (2 + (1 + (- (4 * (4 + 1)) / 2)))).
n=100
for i in $(seq 0 $n)
do
if [ "$i" -eq 0 ]; then
input="- ($n * ($n + 1)) / 2"
else
input="$i + ($input)"
fi
done
echo "$input" > input
run 0 '0'
cat >input <<EOF
() + ()
EOF
run 1 'err: syntax error, unexpected end-of-file, expecting + or - or number or string'
cat >input <<EOF
1 + $
EOF
run 1 'err: syntax error, invalid character: $
err: syntax error, unexpected end-of-line, expecting + or - or number or string'
@@ -0,0 +1,108 @@
/* Scanner for reccalc. -*- C -*-
Copyright (C) 2019-2021 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 <https://www.gnu.org/licenses/>. */
/* Prologue (directives). -*- C -*- */
/* Disable Flex features we don't need, to avoid warnings. */
%option nodefault noinput nounput noyywrap
%option reentrant
%{
#include <assert.h>
#include <limits.h> /* INT_MIN */
#include <stdlib.h> /* strtol */
#include "parse.h"
%}
%x SC_STRING
%%
%{
// Number of opened parentheses.
int nesting = 0;
// A buffer storing the text inside the outer parentheses.
char *str = NULL;
// Its allocated size.
int capacity = 0;
// Its used size.
int size = 0;
#define STR_APPEND() \
do { \
if (capacity < size + yyleng + 1) \
{ \
do \
capacity = capacity ? 2 * capacity : 128; \
while (capacity < size + yyleng + 1); \
str = realloc (str, (size_t) capacity); \
} \
memcpy (str + size, yytext, (size_t) yyleng); \
size += yyleng; \
assert (size < capacity); \
} while (0)
%}
// Rules.
"+" return TOK_PLUS;
"-" return TOK_MINUS;
"*" return TOK_STAR;
"/" return TOK_SLASH;
"(" nesting += 1; BEGIN SC_STRING;
/* Scan an integer. */
[0-9]+ {
errno = 0;
long n = strtol (yytext, NULL, 10);
if (! (INT_MIN <= n && n <= INT_MAX && errno != ERANGE))
yyerror (yyscanner, res, "integer is out of range");
yylval->TOK_NUM = (int) n;
return TOK_NUM;
}
/* Ignore white spaces. */
[ \t]+ continue;
"\n" return TOK_EOL;
. yyerror (yyscanner, res, "syntax error, invalid character: %c", yytext[0]);
<SC_STRING>
{
"("+ nesting += yyleng; STR_APPEND ();
")" {
if (!--nesting)
{
BEGIN INITIAL;
if (str)
str[size] = 0;
yylval->TOK_STR = str;
return TOK_STR;
}
else
STR_APPEND ();
}
[^()]+ STR_APPEND ();
}
<<EOF>> return TOK_EOF;
%%
/* Epilogue (C code). */
@@ -0,0 +1,20 @@
# This Makefile is designed to be simple and readable. It does not
# aim at portability. It requires GNU Make.
BASE = rpcalc
BISON = bison
all: $(BASE)
%.c %.html %.gv: %.y
$(BISON) $(BISONFLAGS) --html --graph -o $*.c $<
%: %.c
$(CC) $(CFLAGS) -o $@ $<
run: $(BASE)
@echo "Type arithmetic expressions in Reverse Polish Notation. Quit with ctrl-d."
./$<
clean:
rm -f $(BASE) $(BASE).c $(BASE).html $(BASE).xml $(BASE).gv
@@ -0,0 +1,45 @@
## Copyright (C) 2005-2006, 2008-2015, 2018-2021 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 <https://www.gnu.org/licenses/>.
## -------------------- ##
## Building & testing. ##
## -------------------- ##
BUILT_SOURCES += $(rpcalc_sources)
CLEANFILES += %D%/rpcalc.[ch] %D%/rpcalc.output
CLEANDIRS += %D%/*.dSYM
rpcalc_extracted = %D%/rpcalc.y
rpcalc_sources = $(rpcalc_extracted)
extracted += $(rpcalc_extracted)
check_PROGRAMS += %D%/rpcalc
nodist_%C%_rpcalc_SOURCES = $(rpcalc_sources)
%D%/rpcalc.c: $(dependencies)
# Don't use gnulib's system headers.
%C%_rpcalc_CPPFLAGS = -I$(top_builddir)/%D%
%C%_rpcalc_CFLAGS = $(TEST_CFLAGS)
%C%_rpcalc_LDADD = -lm
dist_TESTS += %D%/rpcalc.test
## ------------ ##
## Installing. ##
## ------------ ##
rpcalcdir = $(docdir)/%D%
rpcalc_DATA = $(rpcalc_extracted)
dist_rpcalc_DATA = %D%/Makefile
@@ -0,0 +1,46 @@
#! /bin/sh
# Copyright (C) 2005-2015, 2018-2021 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 <https://www.gnu.org/licenses/>.
cat >input <<EOF
1 2 3 * +
EOF
run 0 7
cat >input <<EOF
1.1 2.2 3.3 * +
EOF
run 0 8.36
cat >input <<EOF
1 2 + 3 *
EOF
run 0 9
cat >input <<EOF
1 2 3 4 5 6 7 8 9 * * * * * * * *
EOF
run 0 362880
cat >input <<EOF
3 7 + 3 4 5 * + - n
EOF
run 0 13
cat >input <<EOF
3 4 ^
EOF
run 0 81
@@ -0,0 +1,100 @@
/* Parser for rpcalc. -*- C -*-
Copyright (C) 1988-1993, 1995, 1998-2015, 2018-2021 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 <https://www.gnu.org/licenses/>. */
/* Reverse Polish Notation calculator. */
%{
#include <stdio.h>
#include <math.h>
int yylex (void);
void yyerror (char const *);
%}
%define api.value.type {double}
%token NUM
%% /* Grammar rules and actions follow. */
input:
%empty
| input line
;
line:
'\n'
| exp '\n' { printf ("%.10g\n", $1); }
;
exp:
NUM
| exp exp '+' { $$ = $1 + $2; }
| exp exp '-' { $$ = $1 - $2; }
| exp exp '*' { $$ = $1 * $2; }
| exp exp '/' { $$ = $1 / $2; }
| exp exp '^' { $$ = pow ($1, $2); } /* Exponentiation */
| exp 'n' { $$ = -$1; } /* Unary minus */
;
%%
/* The lexical analyzer returns a double floating point
number on the stack and the token NUM, or the numeric code
of the character read if not a number. It skips all blanks
and tabs, and returns 0 for end-of-input. */
#include <ctype.h>
#include <stdlib.h>
int
yylex (void)
{
int c = getchar ();
/* Skip white space. */
while (c == ' ' || c == '\t')
c = getchar ();
/* Process numbers. */
if (c == '.' || isdigit (c))
{
ungetc (c, stdin);
if (scanf ("%lf", &yylval) != 1)
abort ();
return NUM;
}
/* Return end-of-input. */
else if (c == EOF)
return YYEOF;
/* Return a single char. */
else
return c;
}
int
main (void)
{
return yyparse ();
}
#include <stdio.h>
/* Called by yyparse on error. */
void
yyerror (char const *s)
{
fprintf (stderr, "%s\n", s);
}
@@ -0,0 +1,31 @@
# Examples in D
This directory contains examples of Bison grammar files in D.
You can run `make` to compile these examples. And `make clean` to tidy
afterwards.
## d/simple.y
The usual calculator.
## d/calc.y
A richer implementation of the calculator, with location tracking.
<!---
Local Variables:
fill-column: 76
ispell-dictionary: "american"
End:
Copyright (C) 2018-2021 Free Software Foundation, Inc.
Permission is granted to copy, distribute and/or modify this document
under the terms of the GNU Free Documentation License, Version 1.3 or
any later version published by the Free Software Foundation; with no
Invariant Sections, with no Front-Cover Texts, and with no Back-Cover
Texts. A copy of the license is included in the "GNU Free
Documentation License" file as part of this distribution.
# LocalWords: mfcalc calc parsers yy
--->
@@ -0,0 +1,20 @@
# This Makefile is designed to be simple and readable. It does not
# aim at portability. It requires GNU Make.
BISON = bison
DC = dmd
all: calc
%.d %.html %.gv: %.y
$(BISON) $(BISONFLAGS) --html --graph -o $*.d $<
%: %.d
$(DC) $(DCFLAGS) $<
run: calc
@echo "Type arithmetic expressions. Quit with ctrl-d."
./$<
clean:
rm -f calc calc.d calc.xml calc.gv calc.html *.o
@@ -0,0 +1,51 @@
#! /bin/sh
# Copyright (C) 2018-2021 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 <https://www.gnu.org/licenses/>.
cat >input <<EOF
1 + 2 * 3
EOF
run 0 7
cat >input <<EOF
+1 + +2 * +3
EOF
run 0 7
cat >input <<EOF
-1 + -2 * -3
EOF
run 0 5
cat >input <<EOF
1 + 2 * * 3
EOF
run 1 "err: 1.9: syntax error, unexpected *, expecting + or - or ( or number"
cat >input <<EOF
1111 + 1111 2222
EOF
run 1 "err: 1.13-16: syntax error, unexpected number"
cat >input <<EOF
1 +
EOF
run 1 "err: 1.4-2.0: syntax error, unexpected end of line, expecting + or - or ( or number"
cat >input <<EOF
99 009
EOF
run 1 "err: 1.4-6: syntax error, unexpected number"
@@ -0,0 +1,188 @@
/* Parser and scanner for calc in D. -*- D -*-
Copyright (C) 2018-2021 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 <https://www.gnu.org/licenses/>. */
%language "D"
%define api.parser.class {Calc}
%define api.push-pull push
%define api.token.constructor
%define api.value.type union
%define parse.error detailed
%define parse.trace
%locations
/* Bison Declarations */
%token PLUS "+"
MINUS "-"
STAR "*"
SLASH "/"
LPAR "("
RPAR ")"
EOL "end of line"
%token <int> NUM "number"
%type <int> exp
%printer { yyo.write($$); } <int>
%left "-" "+"
%left "*" "/"
%precedence UNARY /* unary operators */
/* Grammar follows */
%%
input:
line
| input line
;
line:
EOL
| exp EOL { writeln ($exp); }
| error EOL { yyerrok(); }
;
exp:
NUM { $$ = $1; }
| exp "+" exp { $$ = $1 + $3; }
| exp "-" exp { $$ = $1 - $3; }
| exp "*" exp { $$ = $1 * $3; }
| exp "/" exp { $$ = $1 / $3; }
| "+" exp %prec UNARY { $$ = $2; }
| "-" exp %prec UNARY { $$ = -$2; }
| "(" exp ")" { $$ = $2; }
;
%%
import std.range.primitives;
import std.stdio;
auto calcLexer(R)(R range)
if (isInputRange!R && is(ElementType!R : dchar))
{
return new CalcLexer!R(range);
}
auto calcLexer(File f)
{
import std.algorithm : map, joiner;
import std.utf : byDchar;
return f.byChunk(1024) // avoid making a syscall roundtrip per char
.map!(chunk => cast(char[]) chunk) // because byChunk returns ubyte[]
.joiner // combine chunks into a single virtual range of char
.calcLexer; // forward to other overload
}
class CalcLexer(R) : Lexer
if (isInputRange!R && is(ElementType!R : dchar))
{
R input;
this(R r) { input = r; }
Location location;
// Should be a local in main, shared with %parse-param.
int exit_status = 0;
void yyerror(const Location loc, string s)
{
exit_status = 1;
stderr.writeln(loc.toString(), ": ", s);
}
Symbol yylex()
{
import std.uni : isWhite, isNumber;
// Skip initial spaces
while (!input.empty && input.front != '\n' && isWhite(input.front))
{
location.end.column++;
input.popFront;
}
location.step();
if (input.empty)
return Symbol.YYEOF(location);
// Numbers.
if (input.front.isNumber)
{
import std.compiler : version_minor;
static if (version_minor >= 95)
{
// from Dlang v2.095.0 onwards std.conv.parse reports
// the number of consumed characters
import std.typecons : Flag, Yes;
import std.conv : parse;
auto parsed = parse!(int, R, Yes.doCount)(input);
int ival = parsed.data;
location.end.column += cast(int) parsed.count;
}
else
{
auto copy = input;
import std.conv : parse;
int ival = input.parse!int;
while (!input.empty && copy.front != input.front)
{
location.end.column++;
copy.popFront;
}
}
return Symbol.NUM(ival, location);
}
// Individual characters
auto ch = input.front;
input.popFront;
location.end.column++;
switch (ch)
{
case '+': return Symbol.PLUS(location);
case '-': return Symbol.MINUS(location);
case '*': return Symbol.STAR(location);
case '/': return Symbol.SLASH(location);
case '(': return Symbol.LPAR(location);
case ')': return Symbol.RPAR(location);
case '\n':
{
location.end.line++;
location.end.column = 1;
return Symbol.EOL(location);
}
default: assert(0);
}
}
}
int main()
{
auto l = calcLexer(stdin);
auto p = new Calc(l);
import core.stdc.stdlib : getenv;
if (getenv("YYDEBUG"))
p.setDebugLevel(1);
int status;
do {
status = p.pushParse(l.yylex());
} while (status == PUSH_MORE);
return l.exit_status;
}
@@ -0,0 +1,36 @@
## Copyright (C) 2018-2021 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 <https://www.gnu.org/licenses/>.
calcddir = $(docdir)/%D%
## ------ ##
## Calc. ##
## ------ ##
if ENABLE_D
check_SCRIPTS += %D%/calc
TESTS += %D%/calc.test
endif
EXTRA_DIST += %D%/calc.test
%D%/calc.d: %D%/calc.y $(dependencies)
$(AM_V_GEN)$(MKDIR_P) %D%
$(AM_V_at)$(BISON) -o $@ $(srcdir)/%D%/calc.y
%D%/calc: %D%/calc.d
$(AM_V_GEN) $(DC) $(DCFLAGS) -of$@ %D%/calc.d
dist_calcd_DATA = %D%/calc.y %D%/Makefile
CLEANFILES += %D%/calc %D%/calc.[do]
@@ -0,0 +1,20 @@
## Copyright (C) 2018-2021 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 <https://www.gnu.org/licenses/>.
ddir = $(docdir)/%D%
dist_d_DATA = %D%/README.md
include %D%/calc/local.mk
include %D%/simple/local.mk
@@ -0,0 +1,20 @@
# This Makefile is designed to be simple and readable. It does not
# aim at portability. It requires GNU Make.
BISON = bison
DC = dmd
all: calc
%.d %.html %.gv: %.y
$(BISON) $(BISONFLAGS) --html --graph -o $*.d $<
%: %.d
$(DC) $(DCFLAGS) $<
run: calc
@echo "Type arithmetic expressions. Quit with ctrl-d."
./$<
clean:
rm -f calc calc.d calc.xml calc.gv calc.html *.o
@@ -0,0 +1,36 @@
#! /bin/sh
# Copyright (C) 2018-2021 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 <https://www.gnu.org/licenses/>.
cat >input <<EOF
1 + 2 * 3
EOF
run 0 7
cat >input <<EOF
+1 + +2 * +3
EOF
run 0 7
cat >input <<EOF
-1 + -2 * -3
EOF
run 0 5
cat >input <<EOF
1 + 2 * * 3
EOF
run 1 "err: syntax error, unexpected *, expecting + or - or ( or number"
@@ -0,0 +1,146 @@
/* Parser and scanner for calc in D. -*- D -*-
Copyright (C) 2018-2021 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 <https://www.gnu.org/licenses/>. */
%language "D"
%define api.parser.class {Calc}
%define parse.error detailed
%union {
int ival;
}
/* Bison Declarations */
%token PLUS "+"
MINUS "-"
STAR "*"
SLASH "/"
LPAR "("
RPAR ")"
EOL "end of line"
%token <ival> NUM "number"
%type <ival> exp
%left "-" "+"
%left "*" "/"
%precedence UNARY /* unary operators */
/* Grammar follows */
%%
input:
line
| input line
;
line:
EOL
| exp EOL { writeln ($exp); }
| error EOL { yyerrok(); }
;
exp:
NUM { $$ = $1; }
| exp "+" exp { $$ = $1 + $3; }
| exp "-" exp { $$ = $1 - $3; }
| exp "*" exp { $$ = $1 * $3; }
| exp "/" exp { $$ = $1 / $3; }
| "+" exp %prec UNARY { $$ = $2; }
| "-" exp %prec UNARY { $$ = -$2; }
| "(" exp ")" { $$ = $2; }
;
%%
import std.range.primitives;
import std.stdio;
auto calcLexer(R)(R range)
if (isInputRange!R && is(ElementType!R : dchar))
{
return new CalcLexer!R(range);
}
auto calcLexer(File f)
{
import std.algorithm : map, joiner;
import std.utf : byDchar;
return f.byChunk(1024) // avoid making a syscall roundtrip per char
.map!(chunk => cast(char[]) chunk) // because byChunk returns ubyte[]
.joiner // combine chunks into a single virtual range of char
.calcLexer; // forward to other overload
}
class CalcLexer(R) : Lexer
if (isInputRange!R && is(ElementType!R : dchar))
{
R input;
this(R r) { input = r; }
// Should be a local in main, shared with %parse-param.
int exit_status = 0;
public void yyerror(string s)
{
exit_status = 1;
stderr.writeln(s);
}
Symbol yylex()
{
import std.uni : isWhite, isNumber;
// Skip initial spaces
while (!input.empty && input.front != '\n' && isWhite(input.front))
input.popFront;
if (input.empty)
return Symbol(TokenKind.YYEOF);
// Numbers.
if (input.front.isNumber)
{
import std.conv : parse;
return Symbol(TokenKind.NUM, input.parse!int);
}
// Individual characters
auto ch = input.front;
input.popFront;
switch (ch)
{
case '+': return Symbol(TokenKind.PLUS);
case '-': return Symbol(TokenKind.MINUS);
case '*': return Symbol(TokenKind.STAR);
case '/': return Symbol(TokenKind.SLASH);
case '(': return Symbol(TokenKind.LPAR);
case ')': return Symbol(TokenKind.RPAR);
case '\n': return Symbol(TokenKind.EOL);
default: assert(0);
}
}
}
int main()
{
auto l = calcLexer(stdin);
auto p = new Calc(l);
p.parse();
return l.exit_status;
}
@@ -0,0 +1,36 @@
## Copyright (C) 2018-2021 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 <https://www.gnu.org/licenses/>.
simpleddir = $(docdir)/%D%
## ------ ##
## Calc. ##
## ------ ##
if ENABLE_D
check_SCRIPTS += %D%/calc
TESTS += %D%/calc.test
endif
EXTRA_DIST += %D%/calc.test
%D%/calc.d: %D%/calc.y $(dependencies)
$(AM_V_GEN)$(MKDIR_P) %D%
$(AM_V_at)$(BISON) -o $@ $(srcdir)/%D%/calc.y
%D%/calc: %D%/calc.d
$(AM_V_GEN) $(DC) $(DCFLAGS) -of$@ %D%/calc.d
dist_simpled_DATA = %D%/calc.y %D%/Makefile
CLEANFILES += %D%/calc %D%/calc.[do]
+214
View File
@@ -0,0 +1,214 @@
#! /usr/bin/perl -w
# Extract all examples from the manual source.
# This file is part of GNU Bison
# Copyright (C) 1992, 2000-2001, 2005-2006, 2009-2015, 2018-2021 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 <https://www.gnu.org/licenses/>.
# Usage: extexi [OPTION...] input-file.texi ... -- [FILES to extract]
# Look for @example environments preceded with lines such as:
#
# @comment file c/mfcalc/calc.y
# or
# @comment file c/mfcalc/calc.y: 3
#
# and output their content in that file (c/mfcalc/calc.y). When
# numbers are provided, use them to decide the output order (block 1
# is output before block 2, even if the latter appears before). The
# same number may be used several time, in which case the order of
# appearance is used.
#
# Use @ignore for code to extract that must not be part of the
# documentation. For instance:
#
# @ignore
# @comment file: c++/calc++/scanner.ll
# @example
# // Work around an incompatibility in Flex.
# # undef yywrap
# # define yywrap() 1
# @end example
# @end ignore
use strict;
use File::Basename qw(dirname);
use File::Path qw(make_path);
# Whether we generate synclines.
my $synclines = 0;
# normalize($block)
# -----------------
# Remove Texinfo mark up.
sub normalize($)
{
local ($_) = @_;
# If we just remove this lines, then the compiler's tracking of
# #lines is broken. Leave lines that that accepted by all our tools
# (including flex, hence the leading space), and that will be easy
# to remove (see the Make examples-unline recipe).
s{^\@(c |comment|dots|end (ignore|group)|ignore|group).*}{ /**/}mg;
s/\@value\{VERSION\}/$ENV{VERSION}/g;
s/^\@(error|result)\{\}//mg;
s/\@([{}@])/$1/g;
s/\@comment.*//;
$_;
}
# Print messages only once.
my %msg;
sub message($)
{
my ($msg) = @_;
if (! $msg{$msg})
{
print STDERR "extexi: $msg\n";
$msg{$msg} = 1;
}
}
# The list of files we should extract.
my @file_wanted;
# Whether we should extract that file, and then under which path. We
# check if the prefix matches. So for instance if "foo/bar.y" is
# wanted (i.e., in @FILE_WANTED), "file: bar.y" matches.
sub file_wanted ($)
{
my ($f) = @_;
for my $file (@file_wanted)
{
# No endswith in Perl 5...
return $file if $f eq substr($file, -length($f));
}
undef
}
# process ($in)
# -------------
# Read input file $in, and generate the outputs.
sub process ($)
{
my ($in) = @_;
use IO::File;
my $f = new IO::File($in)
or die "$in: cannot open: $?";
# FILE-NAME => { BLOCK-NUM => CODE }
my %file;
# The latest "@comment file: FILE [BLOCK-NUM]" arguments.
my $file;
my $block;
# The @example block currently read.
my $input;
local $_;
while (<$f>)
{
if (/^\@comment file: ([^:\n]+)(?::\s*(\d+))?$/)
{
my $f = $1;
$block = $2 || 1;
if (file_wanted($f))
{
$file = file_wanted($f);
message(" GEN $file");
}
else
{
message("SKIP $f");
}
}
elsif ($file && /^\@(small)?example$/ .. /^\@end (small)?example$/)
{
if (/^\@(small)?example$/)
{
# Bison supports synclines, but not Flex.
$input .= sprintf ("#line %s \"$in\"\n", $. + 1)
if $synclines && $file =~ /\.[chy]*$/;
}
elsif (/^\@end (small)?example$/)
{
die "no contents: $file"
if $input eq "";
$file{$file}{$block} .= "\n" if defined $file{$file}{$block};
$file{$file}{$block} .= normalize($input);
$file = $input = undef;
++$block;
}
else
{
$input .= $_;
}
}
}
# Output the files.
for my $file (keys %file)
{
make_path (dirname ($file));
my $o = new IO::File(">$file")
or die "$file: cannot create: $?";
print $o $file{$file}{$_}
for sort keys %{$file{$file}};
}
}
my @input;
my $seen_dash = 0;
for my $arg (@ARGV)
{
if ($seen_dash)
{
push @file_wanted, $arg;
}
elsif ($arg eq '--')
{
$seen_dash = 1;
}
elsif ($arg eq '--synclines')
{
$synclines = 1;
}
else
{
push @input, $arg;
}
}
process $_
foreach @input;
### Setup "GNU" style for perl-mode and cperl-mode.
## Local Variables:
## perl-indent-level: 2
## perl-continued-statement-offset: 2
## perl-continued-brace-offset: 0
## perl-brace-offset: 0
## perl-brace-imaginary-offset: 0
## perl-label-offset: -2
## cperl-indent-level: 2
## cperl-brace-offset: 0
## cperl-continued-brace-offset: 0
## cperl-label-offset: -2
## cperl-extra-newline-before-brace: t
## cperl-merge-trailing-else: nil
## cperl-continued-statement-offset: 2
## End:
@@ -0,0 +1,30 @@
# Examples in Java
This directory contains examples of Bison grammar files in Java.
You can run `make` to compile these examples. And `make clean` to tidy
afterwards.
## simple/Calc.y
The usual calculator, a very simple version.
## calc/Calc.y
The calculator, but with location tracking, debug traces, and a push parser.
<!---
Local Variables:
mode: markdown
fill-column: 76
ispell-dictionary: "american"
End:
Copyright (C) 2018-2021 Free Software Foundation, Inc.
Permission is granted to copy, distribute and/or modify this document
under the terms of the GNU Free Documentation License, Version 1.3 or
any later version published by the Free Software Foundation; with no
Invariant Sections, with no Front-Cover Texts, and with no Back-Cover
Texts. A copy of the license is included in the "GNU Free
Documentation License" file as part of this distribution.
--->
@@ -0,0 +1,38 @@
#! /bin/sh
# Copyright (C) 2018-2021 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 <https://www.gnu.org/licenses/>.
cat >input <<EOF
1 + 2 * 3
EOF
run 0 7
cat >input <<EOF
1 + 2 * 3 = 7
(1 + 2) * 3 = 9
EOF
run 0 '7
9'
cat >input <<EOF
1 + 2 * * 3
EOF
run 0 "err: 1.9-1.10: syntax error: expected ! or - or ( or number before *"
cat >input <<EOF
12 222
EOF
run 0 "err: 1.6-1.9: syntax error: expected + or - or * or / or ^ or = or end of line before number"
@@ -0,0 +1,317 @@
/* Parser and scanner for calc in Java. -*- Java -*-
Copyright (C) 2018-2021 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 <https://www.gnu.org/licenses/>. */
%language "Java"
%define api.parser.class {Calc}
%define api.parser.public
%define api.push-pull push
// Customized syntax error messages (see reportSyntaxError)...
%define parse.error custom
// ... with locations...
%locations
// ... and accurate list of expected tokens.
%define parse.lac full
%define parse.trace
%code imports {
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.StreamTokenizer;
import java.nio.CharBuffer;
}
%code {
public static void main(String[] args) throws IOException {
CalcLexer scanner = new CalcLexer(System.in);
Calc parser = new Calc(scanner);
for (String arg : args)
if (arg.equals("-p"))
parser.setDebugLevel(1);
int status;
do {
int token = scanner.getToken();
Object lval = scanner.getValue();
Calc.Location yyloc = scanner.getLocation();
status = parser.push_parse(token, lval, yyloc);
} while (status == Calc.YYPUSH_MORE);
if (status != Calc.YYACCEPT)
System.exit(1);
}
static String i18n(String s) {
return s;
}
}
/* Bison Declarations */
%token
BANG "!"
PLUS "+"
MINUS "-"
STAR "*"
SLASH "/"
CARET "^"
LPAREN "("
RPAREN ")"
EQUAL "="
EOL _("end of line")
<Integer>
NUM _("number")
%type <Integer> exp
%nonassoc "=" /* comparison */
%left "-" "+"
%left "*" "/"
%precedence NEG /* negation--unary minus */
%right "^" /* exponentiation */
/* Grammar follows */
%%
input:
line
| input line
;
line:
EOL
| exp EOL { System.out.println($exp); }
| error EOL
;
exp:
NUM { $$ = $1; }
| exp "=" exp
{
if ($1.intValue() != $3.intValue())
yyerror(@$, "calc: error: " + $1 + " != " + $3);
}
| exp "+" exp { $$ = $1 + $3; }
| exp "-" exp { $$ = $1 - $3; }
| exp "*" exp { $$ = $1 * $3; }
| exp "/" exp { $$ = $1 / $3; }
| "-" exp %prec NEG { $$ = -$2; }
| exp "^" exp { $$ = (int) Math.pow($1, $3); }
| "(" exp ")" { $$ = $2; }
| "(" error ")" { $$ = 1111; }
| "!" { $$ = 0; return YYERROR; }
| "-" error { $$ = 0; return YYERROR; }
;
%%
class CalcLexer implements Calc.Lexer {
StreamTokenizer st;
PositionReader reader;
public CalcLexer(InputStream is) {
reader = new PositionReader(new InputStreamReader(is));
st = new StreamTokenizer(reader);
st.resetSyntax();
st.eolIsSignificant(true);
st.wordChars('0', '9');
}
Position start = new Position(1, 0);
Position end = new Position(1, 0);
/**
* The location of the last token read.
* Implemented with getStartPos and getEndPos in pull parsers.
*/
public Calc.Location getLocation() {
return new Calc.Location(new Position(start), new Position(end));
}
/**
* Build and emit a syntax error message.
*/
public void reportSyntaxError(Calc.Context ctx) {
System.err.print(ctx.getLocation() + ": syntax error");
{
final int TOKENMAX = 10;
Calc.SymbolKind[] arg = new Calc.SymbolKind[TOKENMAX];
int n = ctx.getExpectedTokens(arg, TOKENMAX);
for (int i = 0; i < n; ++i)
System.err.print((i == 0 ? ": expected " : " or ")
+ arg[i].getName());
}
{
Calc.SymbolKind lookahead = ctx.getToken();
if (lookahead != null)
System.err.print(" before " + lookahead.getName());
}
System.err.println("");
}
/**
* Emit an error referring to the given location in a user-defined way.
*
* @@param loc The location of the element to which the
* error message is related.
* @@param msg The string for the error message.
*/
public void yyerror(Calc.Location loc, String msg) {
if (loc == null)
System.err.println(msg);
else
System.err.println(loc + ": " + msg);
}
Integer yylval;
/**
* The value of the last token read. Called getLVal in pull parsers.
*/
public Object getValue() {
return yylval;
}
/**
* Fetch the next token. Called yylex in pull parsers.
*/
public int getToken() throws IOException {
start.set(reader.getPosition());
int ttype = st.nextToken();
end.set(reader.getPosition());
switch (ttype) {
case StreamTokenizer.TT_EOF:
return YYEOF;
case StreamTokenizer.TT_EOL:
end.line += 1;
end.column = 0;
return EOL;
case StreamTokenizer.TT_WORD:
yylval = Integer.parseInt(st.sval);
end.set(reader.getPreviousPosition());
return NUM;
case ' ': case '\t':
return getToken();
case '!':
return BANG;
case '+':
return PLUS;
case '-':
return MINUS;
case '*':
return STAR;
case '/':
return SLASH;
case '^':
return CARET;
case '(':
return LPAREN;
case ')':
return RPAREN;
case '=':
return EQUAL;
default:
throw new AssertionError("invalid character: " + ttype);
}
}
}
/**
* A class defining a point in the input.
*/
class Position {
public int line = 1;
public int column = 1;
public Position() {
line = 1;
column = 1;
}
public Position(int l, int t) {
line = l;
column = t;
}
public Position(Position p) {
line = p.line;
column = p.column;
}
public void set(Position p) {
line = p.line;
column = p.column;
}
public boolean equals(Position l) {
return l.line == line && l.column == column;
}
public String toString() {
return Integer.toString(line) + "." + Integer.toString(column);
}
public int line() {
return line;
}
public int column() {
return column;
}
}
/**
* A Stream reader that keeps track of the current Position.
*/
class PositionReader extends BufferedReader {
private Position position = new Position();
// Position before the latest call to "read", i.e. position
// of the last character of the current token.
private Position previousPosition = new Position();
public PositionReader(Reader reader) {
super(reader);
}
public int read() throws IOException {
previousPosition.set(position);
int res = super.read();
if (res > -1) {
char c = (char) res;
if (c == '\r' || c == '\n') {
position.line += 1;
position.column = 1;
} else {
position.column += 1;
}
}
return res;
}
public Position getPosition() {
return position;
}
public Position getPreviousPosition() {
return previousPosition;
}
}
@@ -0,0 +1,21 @@
# This Makefile is designed to be simple and readable. It does not
# aim at portability. It requires GNU Make.
BISON = bison
JAVAC = javac
JAVA = java
all: Calc.class
%.java %.html %.gv: %.y
$(BISON) $(BISONFLAGS) --html --graph -o $*.java $<
%.class: %.java
$(JAVAC) $(JAVACFLAGS) $<
run: Calc.class
@echo "Type arithmetic expressions. Quit with ctrl-d."
$(JAVA) $(JAVAFLAGS) Calc
clean:
rm -f *.class Calc.java Calc.html Calc.xml Calc.gv
@@ -0,0 +1,36 @@
## Copyright (C) 2018-2021 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 <https://www.gnu.org/licenses/>.
java_calcdir = $(docdir)/%D%
## ------ ##
## Calc. ##
## ------ ##
if ENABLE_JAVA
check_SCRIPTS += %D%/Calc.class
TESTS += %D%/Calc.test
endif
EXTRA_DIST += %D%/Calc.test
%D%/Calc.java: %D%/Calc.y $(dependencies)
$(AM_V_GEN)$(MKDIR_P) %D%
$(AM_V_at)$(BISON) -o $@ $(srcdir)/%D%/Calc.y
%D%/Calc.class: %D%/Calc.java
$(AM_V_GEN) $(SHELL) $(top_builddir)/javacomp.sh %D%/Calc.java
dist_java_calc_DATA = %D%/Calc.y %D%/Makefile
CLEANFILES += %D%/*.class %D%/Calc.java
@@ -0,0 +1,20 @@
## Copyright (C) 2020-2021 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 <https://www.gnu.org/licenses/>.
javadir = $(docdir)/%D%
dist_java_DATA = %D%/README.md
include %D%/calc/local.mk
include %D%/simple/local.mk
@@ -0,0 +1,33 @@
#! /bin/sh
# Copyright (C) 2018-2021 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 <https://www.gnu.org/licenses/>.
cat >input <<EOF
1 + 2 * 3
EOF
run 0 7
cat >input <<EOF
1 + 2 * 3 = 7
(1 + 2) * 3 = 9
EOF
run 0 '7
9'
cat >input <<EOF
1 + 2 * * 3
EOF
run 0 "err: syntax error, unexpected '*', expecting number or '-' or '(' or '!'"
@@ -0,0 +1,125 @@
/* Simple parser and scanner in Java. -*- Java -*-
Copyright (C) 2018-2021 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 <https://www.gnu.org/licenses/>. */
%language "Java"
%define api.parser.class {Calc}
%define api.parser.public
%define parse.error verbose
%code imports {
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.StreamTokenizer;
}
%code {
public static void main(String[] args) throws IOException {
CalcLexer l = new CalcLexer(System.in);
Calc p = new Calc(l);
if (!p.parse())
System.exit(1);
}
}
/* Bison Declarations */
%token <Integer> NUM "number"
%type <Integer> exp
%nonassoc '=' /* comparison */
%left '-' '+'
%left '*' '/'
%precedence NEG /* negation--unary minus */
%right '^' /* exponentiation */
/* Grammar follows */
%%
input:
line
| input line
;
line:
'\n'
| exp '\n' { System.out.println($exp); }
| error '\n'
;
exp:
NUM { $$ = $1; }
| exp '=' exp
{
if ($1.intValue() != $3.intValue())
yyerror("calc: error: " + $1 + " != " + $3);
}
| exp '+' exp { $$ = $1 + $3; }
| exp '-' exp { $$ = $1 - $3; }
| exp '*' exp { $$ = $1 * $3; }
| exp '/' exp { $$ = $1 / $3; }
| '-' exp %prec NEG { $$ = -$2; }
| exp '^' exp { $$ = (int) Math.pow($1, $3); }
| '(' exp ')' { $$ = $2; }
| '(' error ')' { $$ = 1111; }
| '!' { $$ = 0; return YYERROR; }
| '-' error { $$ = 0; return YYERROR; }
;
%%
class CalcLexer implements Calc.Lexer {
StreamTokenizer st;
public CalcLexer(InputStream is) {
st = new StreamTokenizer(new InputStreamReader(is));
st.resetSyntax();
st.eolIsSignificant(true);
st.whitespaceChars('\t', '\t');
st.whitespaceChars(' ', ' ');
st.wordChars('0', '9');
}
public void yyerror(String s) {
System.err.println(s);
}
Integer yylval;
public Object getLVal() {
return yylval;
}
public int yylex() throws IOException {
int ttype = st.nextToken();
switch (ttype) {
case StreamTokenizer.TT_EOF:
return YYEOF;
case StreamTokenizer.TT_EOL:
return (int) '\n';
case StreamTokenizer.TT_WORD:
yylval = Integer.parseInt(st.sval);
return NUM;
default:
return ttype;
}
}
}
@@ -0,0 +1,21 @@
# This Makefile is designed to be simple and readable. It does not
# aim at portability. It requires GNU Make.
BISON = bison
JAVAC = javac
JAVA = java
all: Calc.class
%.java %.html %.gv: %.y
$(BISON) $(BISONFLAGS) --html --graph -o $*.java $<
%.class: %.java
$(JAVAC) $(JAVACFLAGS) $<
run: Calc.class
@echo "Type arithmetic expressions. Quit with ctrl-d."
$(JAVA) $(JAVAFLAGS) Calc
clean:
rm -f *.class Calc.java Calc.html Calc.xml Calc.gv
@@ -0,0 +1,36 @@
## Copyright (C) 2018-2021 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 <https://www.gnu.org/licenses/>.
java_simpledir = $(docdir)/%D%
## ------ ##
## Calc. ##
## ------ ##
if ENABLE_JAVA
check_SCRIPTS += %D%/Calc.class
TESTS += %D%/Calc.test
endif
EXTRA_DIST += %D%/Calc.test
%D%/Calc.java: %D%/Calc.y $(dependencies)
$(AM_V_GEN)$(MKDIR_P) %D%
$(AM_V_at)$(BISON) -o $@ $(srcdir)/%D%/Calc.y
%D%/Calc.class: %D%/Calc.java
$(AM_V_GEN) $(SHELL) $(top_builddir)/javacomp.sh %D%/Calc.java
dist_java_simple_DATA = %D%/Calc.y %D%/Makefile
CLEANFILES += %D%/*.class %D%/Calc.java
@@ -0,0 +1,102 @@
## Copyright (C) 2005, 2008-2015, 2018-2021 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 <https://www.gnu.org/licenses/>.
## Because some of our examples use
##
## %C%_reccalc_SOURCES = %D%/parse.y
##
## Automake ships parse.y and parse.c, and possibly parse.h when it
## "understands" that there is one. This is not what we want: ship only
## parser.y. Yet we still want to use Automake to compile the sources
## from parser.y. The easiest seems to use
##
## nodist_%C%_reccalc_SOURCES = %D%/parse.y
##
## together with
##
## dist_reccalc_DATA = %D%/parse.y %D%/scan.l %D%/Makefile %D%/README.md
##
## which guarantees that parse.y is indeed shipped.
dist_noinst_SCRIPTS = %D%/extexi %D%/test
TEST_LOG_COMPILER = $(SHELL) $(top_srcdir)/%D%/test
TEST_CFLAGS = \
$(WARN_CFLAGS) $(WARN_CFLAGS_TEST) $(WERROR_CFLAGS)
AM_CXXFLAGS = \
$(WARN_CXXFLAGS) $(WARN_CXXFLAGS_TEST) $(WERROR_CXXFLAGS)
## ------------ ##
## Extracting. ##
## ------------ ##
doc = $(top_srcdir)/doc/bison.texi
extexi = $(top_srcdir)/%D%/extexi
if ENABLE_GCC_WARNINGS
EXTEXIFLAGS = --synclines
endif
extract = VERSION="$(VERSION)" $(PERL) $(extexi) $(EXTEXIFLAGS) $(doc) --
extracted =
EXTRA_DIST += $(extracted)
MAINTAINERCLEANFILES += $(extracted) %D%/extracted.stamp
%D%/extracted.stamp: $(doc) doc/version.texi $(extexi)
$(AM_V_GEN)rm -f $@ $@.tmp
$(AM_V_at)$(MKDIR_P) %D%
$(AM_V_at)touch $@.tmp
$(AM_V_at)$(extract) $(extracted)
$(AM_V_at)mv $@.tmp $@
$(extracted): %D%/extracted.stamp
@test -f $@ || rm -f %D%/extracted.stamp
@test -f $@ || $(MAKE) $(AM_MAKEFLAGS) %D%/extracted.stamp
## ------ ##
## Dist. ##
## ------ ##
# Ship the stamp file, otherwise it will be recreated, which is what
# we want to avoid.
EXTRA_DIST += %D%/extracted.stamp
# Suppress the #lines from the examples when rolling the tarball, so
# that regular users have readable examples even before installing
# Bison.
dist-hook: examples-unline
.PHONY: examples-unline
examples-unline:
cd $(distdir) && \
perl -pi -0777 -e 's/#line.*\n//g;s{^ /\*\*/\n}{}mg' $(extracted)
## ---------- ##
## Examples. ##
## ---------- ##
examplesdir = $(docdir)/examples
dist_examples_DATA = %D%/README.md
CLEANDIRS += %D%/*.dSYM
.PHONY: check-examples
check-examples: check-TESTS
include %D%/c/local.mk
include %D%/c++/local.mk
include %D%/d/local.mk
include %D%/java/local.mk
+176
View File
@@ -0,0 +1,176 @@
#! /bin/sh
# Copyright (C) 2005-2015, 2018-2021 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 <https://www.gnu.org/licenses/>.
SHELL=/bin/sh
export SHELL
me=$(basename "$1" .test)
medir=$(dirname "$1" | sed -e 's,.*examples/,,')
# Number of the current test.
number=1
# Exit status of this script.
status=0
# top_builddir.
cwd=$(pwd)
# Whether to strip '> ...' lines from the expected output.
# See bistromathic.test.
strip_prompt=false
# If diff supports --strip-trailing-cr, use it, to avoid EOL issues
# when testing Java programs on Windows.
echo "checking for diff --strip-trailing-cr..."
diff_opts=
if diff --strip-trailing-cr "$1" "$1"; then
diff_opts=--strip-trailing-cr
fi
echo "checking for diff --strip-trailing-cr... $diff_opts"
# The exercised program.
abs_medir=$cwd/examples/$medir
if test -x "$abs_medir/$me"; then
prog ()
{
"$abs_medir/$me" "$@"
}
elif test -f "$abs_medir/$me.class"; then
prog ()
{
"$SHELL" "$cwd/javaexec.sh" -cp "$abs_medir" "$me" "$@"
}
else
echo "$me: ERROR: cannot find program to exercise in:"
echo "$me: ERROR: $cwd/examples/$medir/$me"
exit 1
fi
# cleanup
# -------
cleanup ()
{
status=$?
if test -z "$DEBUG"; then
cd "$cwd"
rm -rf $$.dir
fi
exit $status
}
trap cleanup 0 1 2 13 15
mkdir $$.dir
cd $$.dir
# skip [MSG]
# ----------
# Skip this test.
skip ()
{
if test x"$1" != x; then
echo "SKIP: $1"
fi
# See Autoconf's doc on 'trap'.
(exit 77); exit 77
}
# run [-n, -noerr, -t] EXPECTED-EXIT-STATUS EXPECTED-OUTPUT [PARSER-OPTIONS]
# --------------------------------------------------------------------------
# -n: no final end-of-line in expected-output
# -noerr: ignore stderr, otherwise merge it into effective output.
# -t: nuke the possible trailing white spaces in the effective output.
run ()
{
echo=echo
noerr=false
rstrip=false
while true; do
case $1 in
(-n) echo=printf; shift;;
(-noerr) noerr=true; shift;;
(-t) rstrip=true; shift;;
(*) break;;
esac
done
# Expected exit status.
sta_exp=$1
shift
# Expected output.
$echo "$1" |
sed -e 's/Reducing stack by rule .* (line .*):/Reducing stack by rule XX (line XXX):/g' |
if $strip_prompt; then
# An extra EOL added in bistromathic's main. Keep that empty line.
sed -e '/^> ./d;s/^> $//g'
else
cat
fi >exp
shift
# Effective exit status.
sta_eff=0
prog "$@" - <input >out_eff 2>err_eff || sta_eff=$?
# Combine effective output and error streams.
{
if $rstrip; then
sed -e 's/ *$//g' out_eff
else
cat out_eff
fi
if ! $noerr; then
sed -e 's/^/err: /g' \
-e 's/Reducing stack by rule .* (line .*):/Reducing stack by rule XX (line XXX):/g' \
err_eff
fi
} >eff
if test $sta_eff -eq $sta_exp; then
if diff $diff_opts eff exp >/dev/null 2>&1; then
echo "$me: PASS: $number"
else
echo "$me: FAIL: $number"
echo "$me: input:"
sed -e 's/^/ /' input
echo "$me: expected output:"
sed -e 's/^/ /' exp
echo "$me: effective output:"
sed -e 's/^/ /' eff
echo "$me: diff:"
diff $diff_opts -u exp eff | sed -e 's/^/ /'
status=1
fi
else
echo "$me: FAIL: $number (expected status: $sta_exp, effective: $sta_eff)"
cat err_eff
status=1
fi
number=$(expr $number + 1)
}
# We have cd'd one level deeper.
case $1 in
/*) . "$1" || status=2;;
*) . "../$1" || status=2;;
esac
exit $status