mirror of
https://git.savannah.gnu.org/git/bison.git
synced 2026-03-09 20:33:03 +00:00
Teaches bison about a new command line option, --file-prefix-map OLD=NEW
(based on the -ffile-prefix-map option from GCC) which causes it to
replace and file path of OLD in the text of the output file with NEW,
mainly for header guards and comments. The primary use of this is to
make builds reproducible with different input paths, and in particular
the debugging information produced when the source code is compiled. For
example, a distro may know that the bison source code will be located at
"/usr/src/bison" and thus can generate bison files that are reproducible
with the following command:
bison --output=/build/bison/parse.c -d --file-prefix-map=/build/bison/=/usr/src/bison/ parse.y
Importantly, this will change the header guards and #line directives
from:
#ifndef YY_BUILD_BISON_PARSE_H
#line 100 "/build/bison/parse.h"
to
#ifndef YY_USR_SRC_BISON_PARSE_H
#line 100 "/usr/src/bison/parse.h"
which is reproducible.
See https://lists.gnu.org/r/bison-patches/2020-05/msg00016.html
Signed-off-by: Joshua Watt <JPEWhacker@gmail.com>
* src/files.h, src/files.c (spec_mapped_header_file)
(mapped_dir_prefix, map_file_name, add_prefix_map): New.
* src/getargs.c (-M, --file-prefix-map): New option.
* src/output.c (prepare): Define b4_mapped_dir_prefix and
b4_spec_header_file.
* src/scan-skel.l (@ofile@): Output the mapped file name.
* data/skeletons/glr.c, data/skeletons/glr.cc,
* data/skeletons/lalr1.cc, data/skeletons/location.cc,
* data/skeletons/yacc.c:
Adjust.
* doc/bison.texi: Document.
* tests/input.at, tests/output.at: Check.
777 lines
27 KiB
Plaintext
777 lines
27 KiB
Plaintext
# Checking the output filenames. -*- Autotest -*-
|
|
|
|
# Copyright (C) 2000-2002, 2005-2015, 2018-2020 Free Software
|
|
# Foundation, Inc.
|
|
|
|
# This program is free software: you can redistribute it and/or modify
|
|
# it under the terms of the GNU General Public License as published by
|
|
# the Free Software Foundation, either version 3 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
AT_BANNER([[Output file names.]])
|
|
|
|
# AT_CHECK_FILES(EXPECTED-FILES, [IGNORED-FILES])
|
|
# -----------------------------------------------
|
|
# Check that the current directory contains FILE... (sorted).
|
|
m4_define([AT_CHECK_FILES],
|
|
[AT_REQUIRE([[find . -type f |
|
|
"$PERL" -ne '
|
|
s,\./,,; chomp;
|
|
push @file, $_ unless m{^($2|testsuite.log)$};
|
|
END { print join (" ", sort @file), "\n" }']],
|
|
[], [$1
|
|
])])
|
|
|
|
# AT_CHECK_OUTPUT(INPUT-FILE, [DIRECTIVES], [FLAGS], EXPECTED-FILES, [STATUS],
|
|
# [ADDITIONAL-TESTS], [PRE-TESTS])
|
|
# -----------------------------------------------------------------------------
|
|
m4_define([AT_CHECK_OUTPUT],
|
|
[AT_SETUP([[Output files: ]$2 $3])[
|
|
]$7[
|
|
for file in ]$1 $4[; do
|
|
case $file in
|
|
*/*) mkdir -p `echo "$file" | sed 's,/[^/]*,,'`;;
|
|
esac
|
|
done
|
|
]AT_DATA([$1],
|
|
[$2[
|
|
%%
|
|
foo: '0' {};
|
|
]])
|
|
|
|
# There is no AT_DATA_UNQUOTED.
|
|
if $EGREP 'at_dir' $1 >/dev/null; then
|
|
AT_PERL_REQUIRE([-pi -e 's{\$at_dir}'"{$at_group_dir}g" $1])
|
|
# On Cygwin, up to Perl 5.28, 'perl -pi' left a foo.bak file.
|
|
AT_CHECK([rm -f $1.bak])
|
|
fi
|
|
|
|
|
|
AT_BISON_CHECK([$3 $1], [$5], [], [ignore])[
|
|
# Ignore the files non-generated files
|
|
]AT_CHECK_FILES([$4], [$1])[
|
|
]$6[
|
|
]AT_CLEANUP[
|
|
]])
|
|
|
|
AT_CHECK_OUTPUT([foo.y], [], [-dv],
|
|
[foo.output foo.tab.c foo.tab.h])
|
|
|
|
# Some versions of Valgrind (at least valgrind-3.6.0.SVN-Debian) report
|
|
# "fgrep: write error: Bad file descriptor" when stdout is closed, so we
|
|
# skip this test group during maintainer-check-valgrind.
|
|
AT_CHECK_OUTPUT([foo.y], [], [-dv >&-],
|
|
[foo.output foo.tab.c foo.tab.h],
|
|
[], [],
|
|
[AT_CHECK([[case "$PREBISON" in *valgrind*) exit 77;; esac]])])
|
|
|
|
AT_CHECK_OUTPUT([foo.y], [], [-dv -o foo.c],
|
|
[foo.c foo.h foo.output])
|
|
AT_CHECK_OUTPUT([foo.y], [], [-dv -o foo.tab.c],
|
|
[foo.output foo.tab.c foo.tab.h])
|
|
|
|
AT_CHECK_OUTPUT([foo.y], [], [-dv -g --xml --fixed-output-files],
|
|
[y.dot y.output y.tab.c y.tab.h y.xml])
|
|
AT_CHECK_OUTPUT([foo.y], [], [-dv -g --xml -y],
|
|
[y.dot y.output y.tab.c y.tab.h y.xml])
|
|
AT_CHECK_OUTPUT([foo.y], [%require "3.4"], [-dv -g --xml -y],
|
|
[y.gv y.output y.tab.c y.tab.h y.xml])
|
|
# With '-o y.tab.c', we expect 'y.output' etc. (for compatibility with Yacc).
|
|
AT_CHECK_OUTPUT([foo.y], [], [-dv -g --xml -o y.tab.c],
|
|
[y.dot y.output y.tab.c y.tab.h y.xml])
|
|
|
|
AT_CHECK_OUTPUT([foo.y], [], [-dv -b bar],
|
|
[bar.output bar.tab.c bar.tab.h])
|
|
AT_CHECK_OUTPUT([foo.y], [], [-dv -g -o foo.c],
|
|
[foo.c foo.dot foo.h foo.output])
|
|
|
|
|
|
AT_CHECK_OUTPUT([foo.y], [%defines %verbose], [],
|
|
[foo.output foo.tab.c foo.tab.h])
|
|
AT_CHECK_OUTPUT([foo.y], [%defines %verbose %yacc],[],
|
|
[y.output y.tab.c y.tab.h])
|
|
|
|
AT_CHECK_OUTPUT([foo.yy], [%defines %verbose %yacc],[],
|
|
[y.output y.tab.c y.tab.h])
|
|
|
|
# Exercise %output and %file-prefix including deprecated '='
|
|
AT_CHECK_OUTPUT([foo.y], [%file-prefix "bar" %defines %verbose], [],
|
|
[bar.output bar.tab.c bar.tab.h])
|
|
AT_CHECK_OUTPUT([foo.y], [%output "bar.c" %defines %verbose %yacc],[],
|
|
[bar.c bar.h bar.output])
|
|
AT_CHECK_OUTPUT([foo.y],
|
|
[%file-prefix "baz" %output "bar.c" %defines %verbose %yacc],
|
|
[],
|
|
[bar.c bar.h bar.output])
|
|
|
|
|
|
# Check priorities of extension control.
|
|
AT_CHECK_OUTPUT([foo.yy], [%defines %verbose], [],
|
|
[foo.output foo.tab.cc foo.tab.hh])
|
|
|
|
AT_CHECK_OUTPUT([foo.yy], [%defines %verbose ], [-o foo.c],
|
|
[foo.c foo.h foo.output])
|
|
|
|
AT_CHECK_OUTPUT([foo.yy], [],
|
|
[--defines=foo.hpp -o foo.c++],
|
|
[foo.c++ foo.hpp])
|
|
|
|
AT_CHECK_OUTPUT([foo.yy], [%defines "foo.hpp"],
|
|
[-o foo.c++],
|
|
[foo.c++ foo.hpp])
|
|
|
|
AT_CHECK_OUTPUT([foo.yy], [],
|
|
[-o foo.c++ --graph=foo.gph],
|
|
[foo.c++ foo.gph])
|
|
|
|
# Do not generate code when there are early errors (even warnings as
|
|
# errors).
|
|
AT_CHECK_OUTPUT([foo.y], [%type <foo> useless],
|
|
[--defines --graph --xml --report=all -Wall -Werror],
|
|
[foo.dot foo.output foo.xml],
|
|
[1])
|
|
|
|
# Do not generate code when there are late errors (even warnings as
|
|
# errors).
|
|
AT_CHECK_OUTPUT([foo.y], [%define useless],
|
|
[--defines --graph --xml --report=all -Wall -Werror],
|
|
[foo.dot foo.output foo.xml],
|
|
[1])
|
|
|
|
|
|
## ------------ ##
|
|
## C++ output. ##
|
|
## ------------ ##
|
|
|
|
m4_define([AT_CHECK_NO_SUBDIR_PART],
|
|
[# Also make sure that the includes do not refer to the subdirectory.
|
|
AT_CHECK([grep 'include .subdir/' $1.cc], 1, [])
|
|
AT_CHECK([grep 'include .subdir/' $1.hh], 1, [])
|
|
])
|
|
|
|
AT_CHECK_OUTPUT([foo.yy], [%skeleton "lalr1.cc"], [],
|
|
[foo.tab.cc])
|
|
|
|
AT_CHECK_OUTPUT([foo.yy], [%skeleton "lalr1.cc" %verbose], [],
|
|
[foo.output foo.tab.cc])
|
|
|
|
AT_CHECK_OUTPUT([foo.yy], [%skeleton "lalr1.cc" %defines %verbose], [],
|
|
[foo.output foo.tab.cc foo.tab.hh stack.hh])
|
|
|
|
AT_CHECK_OUTPUT([foo.yy], [%skeleton "lalr1.cc" %verbose %locations], [],
|
|
[foo.output foo.tab.cc])
|
|
|
|
AT_CHECK_OUTPUT([foo.yy], [%skeleton "lalr1.cc" %defines %verbose %locations], [],
|
|
[foo.output foo.tab.cc foo.tab.hh location.hh position.hh stack.hh])
|
|
|
|
AT_CHECK_OUTPUT([subdir/foo.yy], [%skeleton "lalr1.cc" %defines %verbose], [],
|
|
[foo.output foo.tab.cc foo.tab.hh stack.hh],
|
|
[], [AT_CHECK_NO_SUBDIR_PART([foo.tab])])
|
|
|
|
AT_CHECK_OUTPUT([subdir/foo.yy], [%skeleton "lalr1.cc" %defines %verbose %locations],
|
|
[-o subdir/foo.cc],
|
|
[subdir/foo.cc subdir/foo.hh subdir/foo.output subdir/location.hh subdir/position.hh subdir/stack.hh],
|
|
[], [AT_CHECK_NO_SUBDIR_PART([subdir/foo])])
|
|
|
|
AT_CHECK_OUTPUT([gram_dir/foo.yy],
|
|
[%skeleton "lalr1.cc" %defines %verbose %file-prefix "output_dir/foo"],
|
|
[],
|
|
[output_dir/foo.output output_dir/foo.tab.cc output_dir/foo.tab.hh output_dir/stack.hh])
|
|
|
|
AT_CHECK_OUTPUT([gram_dir/foo.yy],
|
|
[%skeleton "lalr1.cc" %defines %locations %verbose %file-prefix "output_dir/foo"],
|
|
[],
|
|
[output_dir/foo.output output_dir/foo.tab.cc output_dir/foo.tab.hh output_dir/location.hh output_dir/position.hh output_dir/stack.hh])
|
|
|
|
# %require "3.2" => no position.hh not stack.hh.
|
|
AT_CHECK_OUTPUT([foo.yy],
|
|
[%skeleton "lalr1.cc" %defines %locations %define api.location.file none %require "3.2"],
|
|
[],
|
|
[foo.tab.cc foo.tab.hh])
|
|
|
|
AT_CHECK_OUTPUT([foo.yy],
|
|
[%skeleton "lalr1.cc" %defines %locations %define api.location.file "foo.loc.hh" %require "3.2"],
|
|
[],
|
|
[foo.loc.hh foo.tab.cc foo.tab.hh])
|
|
|
|
# Absolute paths.
|
|
AT_CHECK_OUTPUT([foo.yy],
|
|
[%skeleton "lalr1.cc" %defines %locations %define api.location.file "$at_dir/foo.loc.hh" %require "3.2"],
|
|
[],
|
|
[foo.loc.hh foo.tab.cc foo.tab.hh])
|
|
|
|
|
|
# AT_CHECK_CONFLICTING_OUTPUT(INPUT-FILE, DIRECTIVES, FLAGS, STDERR,
|
|
# [EXIT-STATUS])
|
|
# ------------------------------------------------------------------
|
|
m4_define([AT_CHECK_CONFLICTING_OUTPUT],
|
|
[AT_SETUP([Conflicting output files: $2 $3])
|
|
case "$1" in
|
|
*/*) mkdir `echo "$1" | sed 's,/.*,,'`;;
|
|
esac
|
|
AT_DATA([$1],
|
|
[[$2
|
|
%%
|
|
foo: {};
|
|
]])
|
|
|
|
[cp ]$1[ expout]
|
|
# Because an output file name conflict is still a warning, Bison exits
|
|
# with status 0, so AT_BISON_CHECK does not realize that there may be no
|
|
# output file against which to check the XML. AT_BISON_CHECK_NO_XML
|
|
# avoids that problem.
|
|
AT_BISON_CHECK_NO_XML([$3 $1], $5, [], [$4])
|
|
AT_CHECK([[cat $1]], [[0]], [expout])
|
|
AT_CLEANUP
|
|
])
|
|
|
|
AT_CHECK_CONFLICTING_OUTPUT([foo.y],
|
|
[], [--graph="foo.tab.c"],
|
|
[[foo.y: warning: conflicting outputs to file 'foo.tab.c' [-Wother]
|
|
]])
|
|
|
|
AT_CHECK_CONFLICTING_OUTPUT([foo.y],
|
|
[%defines "foo.output"], [-v],
|
|
[[foo.y: warning: conflicting outputs to file 'foo.output' [-Wother]
|
|
]])
|
|
|
|
AT_CHECK_CONFLICTING_OUTPUT([foo.y],
|
|
[%skeleton "lalr1.cc" %defines %locations], [--graph="location.hh"],
|
|
[[foo.y: warning: conflicting outputs to file 'location.hh' [-Wother]
|
|
]])
|
|
|
|
AT_CHECK_CONFLICTING_OUTPUT([foo.y], [], [-o foo.y],
|
|
[[foo.y: error: refusing to overwrite the input file 'foo.y'
|
|
]], 1)
|
|
|
|
|
|
# AT_CHECK_OUTPUT_FILE_NAME(FILE-NAME-PREFIX, [ADDITIONAL-TESTS])
|
|
# ---------------------------------------------------------------
|
|
m4_define([AT_CHECK_OUTPUT_FILE_NAME],
|
|
[AT_SETUP([Output file name: $1])
|
|
|
|
AT_BISON_OPTION_PUSHDEFS
|
|
# Skip if platform doesn't support file name. For example, Cygwin
|
|
# doesn't support file names containing ":" or "\".
|
|
AT_REQUIRE([[touch "]AS_ESCAPE([$1[.tmp]])["]])
|
|
|
|
AT_DATA_GRAMMAR([glr.y],
|
|
[[%glr-parser
|
|
%code {
|
|
]AT_YYERROR_DECLARE_EXTERN[
|
|
]AT_YYLEX_DECLARE_EXTERN[
|
|
}
|
|
%%
|
|
start: {};
|
|
]])
|
|
AT_BISON_CHECK([-o "AS_ESCAPE([$1.c])" --defines="AS_ESCAPE([$1.h])" glr.y])
|
|
AT_CHECK([ls "AS_ESCAPE([$1.c])" "AS_ESCAPE([$1.h])"], [], [ignore])
|
|
AT_COMPILE([glr.o], [-c "AS_ESCAPE([$1.c])"])
|
|
$2
|
|
|
|
AT_DATA_GRAMMAR([cxx.y],
|
|
[[%skeleton "lalr1.cc"
|
|
%code { int yylex (yy::parser::semantic_type*); }
|
|
%%
|
|
start: {};
|
|
]])
|
|
AT_BISON_CHECK([-o "AS_ESCAPE([$1.cc])" --defines="AS_ESCAPE([$1.hh])" cxx.y])
|
|
AT_CHECK([ls "AS_ESCAPE([$1.cc])" "AS_ESCAPE([$1.hh])"], [], [ignore])
|
|
AT_COMPILE_CXX([cxx.o], [-c "AS_ESCAPE([$1.cc])"])
|
|
$2
|
|
|
|
AT_BISON_OPTION_POPDEFS
|
|
AT_CLEANUP
|
|
])
|
|
|
|
# Notice that the header file name here cannot contain
|
|
# '"' since FILENAME in '#include "FILENAME"' cannot.
|
|
AT_CHECK_OUTPUT_FILE_NAME([[`~!@#$%^&*()-=_+{}[]|\:;<>, .']])
|
|
dnl Work around a bug in m4_expand that broke AT_SETUP in autoconf 2.62,
|
|
dnl by using the definition from 2.63.
|
|
m4_version_prereq([2.63], [],
|
|
[m4_define([m4_expand], [_$0(-=<{($1)}>=-)])
|
|
m4_define([_m4_expand],
|
|
[m4_changequote([-=<{(], [)}>=-])$1m4_changequote([, ])])])
|
|
AT_CHECK_OUTPUT_FILE_NAME([[(]])
|
|
AT_CHECK_OUTPUT_FILE_NAME([[)]])
|
|
AT_CHECK_OUTPUT_FILE_NAME([[@%:@]])
|
|
AT_CHECK_OUTPUT_FILE_NAME([[@@]])
|
|
AT_CHECK_OUTPUT_FILE_NAME([[@{]])
|
|
AT_CHECK_OUTPUT_FILE_NAME([[@}]])
|
|
AT_CHECK_OUTPUT_FILE_NAME([[@<:@]])
|
|
AT_CHECK_OUTPUT_FILE_NAME([[@:>@]])
|
|
|
|
|
|
# AT_TEST(SETUP-NAME, GRAMMAR, DOT-BODY)
|
|
# --------------------------------------
|
|
# Check that the DOT graph for GRAMMAR is DOT-BODY.
|
|
m4_pushdef([AT_TEST],
|
|
[AT_SETUP([$1])
|
|
AT_KEYWORDS([[graph]])
|
|
AT_DATA([[input.y]], [$2])
|
|
AT_BISON_CHECK([[-rall --graph input.y]], [0], [[]], [[ignore]])
|
|
AT_CHECK([[grep -v // input.dot]], [0],
|
|
[[
|
|
digraph "input.y"
|
|
{
|
|
node [fontname = courier, shape = box, colorscheme = paired6]
|
|
edge [fontname = courier]
|
|
]$3[}
|
|
]])
|
|
AT_CLEANUP
|
|
])
|
|
|
|
|
|
## ------------------------ ##
|
|
## Graph with no conflicts. ##
|
|
## ------------------------ ##
|
|
|
|
AT_TEST([Graph with no conflicts],
|
|
[[%%
|
|
exp: a '?' b;
|
|
a: ;
|
|
b: 'b';
|
|
]],
|
|
[[
|
|
0 [label="State 0\n\l 0 $accept: . exp $end\l 1 exp: . a '?' b\l 2 a: . %empty\l"]
|
|
0 -> 1 [style=dashed label="exp"]
|
|
0 -> 2 [style=dashed label="a"]
|
|
0 -> "0R2" [style=solid]
|
|
"0R2" [label="R2", fillcolor=3, shape=diamond, style=filled]
|
|
1 [label="State 1\n\l 0 $accept: exp . $end\l"]
|
|
1 -> 3 [style=solid label="$end"]
|
|
2 [label="State 2\n\l 1 exp: a . '?' b\l"]
|
|
2 -> 4 [style=solid label="'?'"]
|
|
3 [label="State 3\n\l 0 $accept: exp $end .\l"]
|
|
3 -> "3R0" [style=solid]
|
|
"3R0" [label="Acc", fillcolor=1, shape=diamond, style=filled]
|
|
4 [label="State 4\n\l 1 exp: a '?' . b\l 3 b: . 'b'\l"]
|
|
4 -> 5 [style=solid label="'b'"]
|
|
4 -> 6 [style=dashed label="b"]
|
|
5 [label="State 5\n\l 3 b: 'b' .\l"]
|
|
5 -> "5R3" [style=solid]
|
|
"5R3" [label="R3", fillcolor=3, shape=diamond, style=filled]
|
|
6 [label="State 6\n\l 1 exp: a '?' b .\l"]
|
|
6 -> "6R1" [style=solid]
|
|
"6R1" [label="R1", fillcolor=3, shape=diamond, style=filled]
|
|
]])
|
|
|
|
## ------------------------ ##
|
|
## Graph with unsolved S/R. ##
|
|
## ------------------------ ##
|
|
|
|
AT_TEST([Graph with unsolved S/R],
|
|
[[%%
|
|
start:
|
|
'a'
|
|
| empty_a 'a'
|
|
| 'b'
|
|
| empty_b 'b'
|
|
| 'c'
|
|
| empty_c 'c'
|
|
;
|
|
empty_a: %prec 'a';
|
|
empty_b: %prec 'b';
|
|
empty_c: %prec 'c';
|
|
]],
|
|
[[
|
|
0 [label="State 0\n\l 0 $accept: . start $end\l 1 start: . 'a'\l 2 | . empty_a 'a'\l 3 | . 'b'\l 4 | . empty_b 'b'\l 5 | . 'c'\l 6 | . empty_c 'c'\l 7 empty_a: . %empty ['a']\l 8 empty_b: . %empty ['b']\l 9 empty_c: . %empty ['c']\l"]
|
|
0 -> 1 [style=solid label="'a'"]
|
|
0 -> 2 [style=solid label="'b'"]
|
|
0 -> 3 [style=solid label="'c'"]
|
|
0 -> 4 [style=dashed label="start"]
|
|
0 -> 5 [style=dashed label="empty_a"]
|
|
0 -> 6 [style=dashed label="empty_b"]
|
|
0 -> 7 [style=dashed label="empty_c"]
|
|
0 -> "0R7d" [label="['a']", style=solid]
|
|
"0R7d" [label="R7", fillcolor=5, shape=diamond, style=filled]
|
|
0 -> "0R8d" [label="['b']", style=solid]
|
|
"0R8d" [label="R8", fillcolor=5, shape=diamond, style=filled]
|
|
0 -> "0R9d" [label="['c']", style=solid]
|
|
"0R9d" [label="R9", fillcolor=5, shape=diamond, style=filled]
|
|
1 [label="State 1\n\l 1 start: 'a' .\l"]
|
|
1 -> "1R1" [style=solid]
|
|
"1R1" [label="R1", fillcolor=3, shape=diamond, style=filled]
|
|
2 [label="State 2\n\l 3 start: 'b' .\l"]
|
|
2 -> "2R3" [style=solid]
|
|
"2R3" [label="R3", fillcolor=3, shape=diamond, style=filled]
|
|
3 [label="State 3\n\l 5 start: 'c' .\l"]
|
|
3 -> "3R5" [style=solid]
|
|
"3R5" [label="R5", fillcolor=3, shape=diamond, style=filled]
|
|
4 [label="State 4\n\l 0 $accept: start . $end\l"]
|
|
4 -> 8 [style=solid label="$end"]
|
|
5 [label="State 5\n\l 2 start: empty_a . 'a'\l"]
|
|
5 -> 9 [style=solid label="'a'"]
|
|
6 [label="State 6\n\l 4 start: empty_b . 'b'\l"]
|
|
6 -> 10 [style=solid label="'b'"]
|
|
7 [label="State 7\n\l 6 start: empty_c . 'c'\l"]
|
|
7 -> 11 [style=solid label="'c'"]
|
|
8 [label="State 8\n\l 0 $accept: start $end .\l"]
|
|
8 -> "8R0" [style=solid]
|
|
"8R0" [label="Acc", fillcolor=1, shape=diamond, style=filled]
|
|
9 [label="State 9\n\l 2 start: empty_a 'a' .\l"]
|
|
9 -> "9R2" [style=solid]
|
|
"9R2" [label="R2", fillcolor=3, shape=diamond, style=filled]
|
|
10 [label="State 10\n\l 4 start: empty_b 'b' .\l"]
|
|
10 -> "10R4" [style=solid]
|
|
"10R4" [label="R4", fillcolor=3, shape=diamond, style=filled]
|
|
11 [label="State 11\n\l 6 start: empty_c 'c' .\l"]
|
|
11 -> "11R6" [style=solid]
|
|
"11R6" [label="R6", fillcolor=3, shape=diamond, style=filled]
|
|
]])
|
|
|
|
## ---------------------- ##
|
|
## Graph with solved S/R. ##
|
|
## ---------------------- ##
|
|
|
|
AT_TEST([Graph with solved S/R],
|
|
[[%left 'a'
|
|
%right 'b'
|
|
%right 'c'
|
|
%%
|
|
start:
|
|
'a'
|
|
| empty_a 'a'
|
|
| 'b'
|
|
| empty_b 'b'
|
|
| 'c'
|
|
| empty_c 'c'
|
|
;
|
|
empty_a: %prec 'a';
|
|
empty_b: %prec 'b';
|
|
empty_c: %prec 'c';
|
|
]],
|
|
[[
|
|
0 [label="State 0\n\l 0 $accept: . start $end\l 1 start: . 'a'\l 2 | . empty_a 'a'\l 3 | . 'b'\l 4 | . empty_b 'b'\l 5 | . 'c'\l 6 | . empty_c 'c'\l 7 empty_a: . %empty ['a']\l 8 empty_b: . %empty []\l 9 empty_c: . %empty []\l"]
|
|
0 -> 1 [style=solid label="'b'"]
|
|
0 -> 2 [style=solid label="'c'"]
|
|
0 -> 3 [style=dashed label="start"]
|
|
0 -> 4 [style=dashed label="empty_a"]
|
|
0 -> 5 [style=dashed label="empty_b"]
|
|
0 -> 6 [style=dashed label="empty_c"]
|
|
0 -> "0R7" [style=solid]
|
|
"0R7" [label="R7", fillcolor=3, shape=diamond, style=filled]
|
|
1 [label="State 1\n\l 3 start: 'b' .\l"]
|
|
1 -> "1R3" [style=solid]
|
|
"1R3" [label="R3", fillcolor=3, shape=diamond, style=filled]
|
|
2 [label="State 2\n\l 5 start: 'c' .\l"]
|
|
2 -> "2R5" [style=solid]
|
|
"2R5" [label="R5", fillcolor=3, shape=diamond, style=filled]
|
|
3 [label="State 3\n\l 0 $accept: start . $end\l"]
|
|
3 -> 7 [style=solid label="$end"]
|
|
4 [label="State 4\n\l 2 start: empty_a . 'a'\l"]
|
|
4 -> 8 [style=solid label="'a'"]
|
|
5 [label="State 5\n\l 4 start: empty_b . 'b'\l"]
|
|
5 -> 9 [style=solid label="'b'"]
|
|
6 [label="State 6\n\l 6 start: empty_c . 'c'\l"]
|
|
6 -> 10 [style=solid label="'c'"]
|
|
7 [label="State 7\n\l 0 $accept: start $end .\l"]
|
|
7 -> "7R0" [style=solid]
|
|
"7R0" [label="Acc", fillcolor=1, shape=diamond, style=filled]
|
|
8 [label="State 8\n\l 2 start: empty_a 'a' .\l"]
|
|
8 -> "8R2" [style=solid]
|
|
"8R2" [label="R2", fillcolor=3, shape=diamond, style=filled]
|
|
9 [label="State 9\n\l 4 start: empty_b 'b' .\l"]
|
|
9 -> "9R4" [style=solid]
|
|
"9R4" [label="R4", fillcolor=3, shape=diamond, style=filled]
|
|
10 [label="State 10\n\l 6 start: empty_c 'c' .\l"]
|
|
10 -> "10R6" [style=solid]
|
|
"10R6" [label="R6", fillcolor=3, shape=diamond, style=filled]
|
|
]])
|
|
|
|
## ---------------- ##
|
|
## Graph with R/R. ##
|
|
## ---------------- ##
|
|
|
|
AT_TEST([Graph with R/R],
|
|
[[%%
|
|
exp: a | b;
|
|
a: ;
|
|
b: ;
|
|
]],
|
|
[[
|
|
0 [label="State 0\n\l 0 $accept: . exp $end\l 1 exp: . a\l 2 | . b\l 3 a: . %empty [$end]\l 4 b: . %empty [$end]\l"]
|
|
0 -> 1 [style=dashed label="exp"]
|
|
0 -> 2 [style=dashed label="a"]
|
|
0 -> 3 [style=dashed label="b"]
|
|
0 -> "0R3" [style=solid]
|
|
"0R3" [label="R3", fillcolor=3, shape=diamond, style=filled]
|
|
0 -> "0R4d" [label="[$end]", style=solid]
|
|
"0R4d" [label="R4", fillcolor=5, shape=diamond, style=filled]
|
|
1 [label="State 1\n\l 0 $accept: exp . $end\l"]
|
|
1 -> 4 [style=solid label="$end"]
|
|
2 [label="State 2\n\l 1 exp: a .\l"]
|
|
2 -> "2R1" [style=solid]
|
|
"2R1" [label="R1", fillcolor=3, shape=diamond, style=filled]
|
|
3 [label="State 3\n\l 2 exp: b .\l"]
|
|
3 -> "3R2" [style=solid]
|
|
"3R2" [label="R2", fillcolor=3, shape=diamond, style=filled]
|
|
4 [label="State 4\n\l 0 $accept: exp $end .\l"]
|
|
4 -> "4R0" [style=solid]
|
|
"4R0" [label="Acc", fillcolor=1, shape=diamond, style=filled]
|
|
]])
|
|
|
|
## ---------------------------------------- ##
|
|
## Graph with reductions with multiple LAT. ##
|
|
## ---------------------------------------- ##
|
|
|
|
AT_TEST([Graph with reductions with multiple LAT],
|
|
[[%%
|
|
exp: a ';' | a ';' | a '.' | b '?' | b '!' | c '?' | c ';';
|
|
a: ;
|
|
b: ;
|
|
c: ;
|
|
]],
|
|
[[
|
|
0 [label="State 0\n\l 0 $accept: . exp $end\l 1 exp: . a ';'\l 2 | . a ';'\l 3 | . a '.'\l 4 | . b '?'\l 5 | . b '!'\l 6 | . c '?'\l 7 | . c ';'\l 8 a: . %empty [';', '.']\l 9 b: . %empty ['?', '!']\l 10 c: . %empty [';', '?']\l"]
|
|
0 -> 1 [style=dashed label="exp"]
|
|
0 -> 2 [style=dashed label="a"]
|
|
0 -> 3 [style=dashed label="b"]
|
|
0 -> 4 [style=dashed label="c"]
|
|
0 -> "0R8" [style=solid]
|
|
"0R8" [label="R8", fillcolor=3, shape=diamond, style=filled]
|
|
0 -> "0R9" [label="['?', '!']", style=solid]
|
|
"0R9" [label="R9", fillcolor=3, shape=diamond, style=filled]
|
|
0 -> "0R10d" [label="[';', '?']", style=solid]
|
|
"0R10d" [label="R10", fillcolor=5, shape=diamond, style=filled]
|
|
1 [label="State 1\n\l 0 $accept: exp . $end\l"]
|
|
1 -> 5 [style=solid label="$end"]
|
|
2 [label="State 2\n\l 1 exp: a . ';'\l 2 | a . ';'\l 3 | a . '.'\l"]
|
|
2 -> 6 [style=solid label="';'"]
|
|
2 -> 7 [style=solid label="'.'"]
|
|
3 [label="State 3\n\l 4 exp: b . '?'\l 5 | b . '!'\l"]
|
|
3 -> 8 [style=solid label="'?'"]
|
|
3 -> 9 [style=solid label="'!'"]
|
|
4 [label="State 4\n\l 6 exp: c . '?'\l 7 | c . ';'\l"]
|
|
4 -> 10 [style=solid label="';'"]
|
|
4 -> 11 [style=solid label="'?'"]
|
|
5 [label="State 5\n\l 0 $accept: exp $end .\l"]
|
|
5 -> "5R0" [style=solid]
|
|
"5R0" [label="Acc", fillcolor=1, shape=diamond, style=filled]
|
|
6 [label="State 6\n\l 1 exp: a ';' . [$end]\l 2 | a ';' . [$end]\l"]
|
|
6 -> "6R1" [style=solid]
|
|
"6R1" [label="R1", fillcolor=3, shape=diamond, style=filled]
|
|
6 -> "6R2d" [label="[$end]", style=solid]
|
|
"6R2d" [label="R2", fillcolor=5, shape=diamond, style=filled]
|
|
7 [label="State 7\n\l 3 exp: a '.' .\l"]
|
|
7 -> "7R3" [style=solid]
|
|
"7R3" [label="R3", fillcolor=3, shape=diamond, style=filled]
|
|
8 [label="State 8\n\l 4 exp: b '?' .\l"]
|
|
8 -> "8R4" [style=solid]
|
|
"8R4" [label="R4", fillcolor=3, shape=diamond, style=filled]
|
|
9 [label="State 9\n\l 5 exp: b '!' .\l"]
|
|
9 -> "9R5" [style=solid]
|
|
"9R5" [label="R5", fillcolor=3, shape=diamond, style=filled]
|
|
10 [label="State 10\n\l 7 exp: c ';' .\l"]
|
|
10 -> "10R7" [style=solid]
|
|
"10R7" [label="R7", fillcolor=3, shape=diamond, style=filled]
|
|
11 [label="State 11\n\l 6 exp: c '?' .\l"]
|
|
11 -> "11R6" [style=solid]
|
|
"11R6" [label="R6", fillcolor=3, shape=diamond, style=filled]
|
|
]])
|
|
|
|
## ------------------------------------------------------ ##
|
|
## Graph with a reduction rule both enabled and disabled. ##
|
|
## ------------------------------------------------------ ##
|
|
|
|
AT_TEST([Graph with a reduction rule both enabled and disabled],
|
|
[[%%
|
|
exp: ifexp | opexp | imm;
|
|
ifexp: "if" exp "then" exp elseexp;
|
|
elseexp: "else" exp | ;
|
|
opexp: exp '+' exp;
|
|
imm: '0';
|
|
]],
|
|
[[
|
|
0 [label="State 0\n\l 0 $accept: . exp $end\l 1 exp: . ifexp\l 2 | . opexp\l 3 | . imm\l 4 ifexp: . \"if\" exp \"then\" exp elseexp\l 7 opexp: . exp '+' exp\l 8 imm: . '0'\l"]
|
|
0 -> 1 [style=solid label="\"if\""]
|
|
0 -> 2 [style=solid label="'0'"]
|
|
0 -> 3 [style=dashed label="exp"]
|
|
0 -> 4 [style=dashed label="ifexp"]
|
|
0 -> 5 [style=dashed label="opexp"]
|
|
0 -> 6 [style=dashed label="imm"]
|
|
1 [label="State 1\n\l 1 exp: . ifexp\l 2 | . opexp\l 3 | . imm\l 4 ifexp: . \"if\" exp \"then\" exp elseexp\l 4 | \"if\" . exp \"then\" exp elseexp\l 7 opexp: . exp '+' exp\l 8 imm: . '0'\l"]
|
|
1 -> 1 [style=solid label="\"if\""]
|
|
1 -> 2 [style=solid label="'0'"]
|
|
1 -> 7 [style=dashed label="exp"]
|
|
1 -> 4 [style=dashed label="ifexp"]
|
|
1 -> 5 [style=dashed label="opexp"]
|
|
1 -> 6 [style=dashed label="imm"]
|
|
2 [label="State 2\n\l 8 imm: '0' .\l"]
|
|
2 -> "2R8" [style=solid]
|
|
"2R8" [label="R8", fillcolor=3, shape=diamond, style=filled]
|
|
3 [label="State 3\n\l 0 $accept: exp . $end\l 7 opexp: exp . '+' exp\l"]
|
|
3 -> 8 [style=solid label="$end"]
|
|
3 -> 9 [style=solid label="'+'"]
|
|
4 [label="State 4\n\l 1 exp: ifexp .\l"]
|
|
4 -> "4R1" [style=solid]
|
|
"4R1" [label="R1", fillcolor=3, shape=diamond, style=filled]
|
|
5 [label="State 5\n\l 2 exp: opexp .\l"]
|
|
5 -> "5R2" [style=solid]
|
|
"5R2" [label="R2", fillcolor=3, shape=diamond, style=filled]
|
|
6 [label="State 6\n\l 3 exp: imm .\l"]
|
|
6 -> "6R3" [style=solid]
|
|
"6R3" [label="R3", fillcolor=3, shape=diamond, style=filled]
|
|
7 [label="State 7\n\l 4 ifexp: \"if\" exp . \"then\" exp elseexp\l 7 opexp: exp . '+' exp\l"]
|
|
7 -> 10 [style=solid label="\"then\""]
|
|
7 -> 9 [style=solid label="'+'"]
|
|
8 [label="State 8\n\l 0 $accept: exp $end .\l"]
|
|
8 -> "8R0" [style=solid]
|
|
"8R0" [label="Acc", fillcolor=1, shape=diamond, style=filled]
|
|
9 [label="State 9\n\l 1 exp: . ifexp\l 2 | . opexp\l 3 | . imm\l 4 ifexp: . \"if\" exp \"then\" exp elseexp\l 7 opexp: . exp '+' exp\l 7 | exp '+' . exp\l 8 imm: . '0'\l"]
|
|
9 -> 1 [style=solid label="\"if\""]
|
|
9 -> 2 [style=solid label="'0'"]
|
|
9 -> 11 [style=dashed label="exp"]
|
|
9 -> 4 [style=dashed label="ifexp"]
|
|
9 -> 5 [style=dashed label="opexp"]
|
|
9 -> 6 [style=dashed label="imm"]
|
|
10 [label="State 10\n\l 1 exp: . ifexp\l 2 | . opexp\l 3 | . imm\l 4 ifexp: . \"if\" exp \"then\" exp elseexp\l 4 | \"if\" exp \"then\" . exp elseexp\l 7 opexp: . exp '+' exp\l 8 imm: . '0'\l"]
|
|
10 -> 1 [style=solid label="\"if\""]
|
|
10 -> 2 [style=solid label="'0'"]
|
|
10 -> 12 [style=dashed label="exp"]
|
|
10 -> 4 [style=dashed label="ifexp"]
|
|
10 -> 5 [style=dashed label="opexp"]
|
|
10 -> 6 [style=dashed label="imm"]
|
|
11 [label="State 11\n\l 7 opexp: exp . '+' exp\l 7 | exp '+' exp . [$end, \"then\", \"else\", '+']\l"]
|
|
11 -> 9 [style=solid label="'+'"]
|
|
11 -> "11R7d" [label="['+']", style=solid]
|
|
"11R7d" [label="R7", fillcolor=5, shape=diamond, style=filled]
|
|
11 -> "11R7" [style=solid]
|
|
"11R7" [label="R7", fillcolor=3, shape=diamond, style=filled]
|
|
12 [label="State 12\n\l 4 ifexp: \"if\" exp \"then\" exp . elseexp\l 5 elseexp: . \"else\" exp\l 6 | . %empty [$end, \"then\", \"else\", '+']\l 7 opexp: exp . '+' exp\l"]
|
|
12 -> 13 [style=solid label="\"else\""]
|
|
12 -> 9 [style=solid label="'+'"]
|
|
12 -> 14 [style=dashed label="elseexp"]
|
|
12 -> "12R6d" [label="[\"else\", '+']", style=solid]
|
|
"12R6d" [label="R6", fillcolor=5, shape=diamond, style=filled]
|
|
12 -> "12R6" [style=solid]
|
|
"12R6" [label="R6", fillcolor=3, shape=diamond, style=filled]
|
|
13 [label="State 13\n\l 1 exp: . ifexp\l 2 | . opexp\l 3 | . imm\l 4 ifexp: . \"if\" exp \"then\" exp elseexp\l 5 elseexp: \"else\" . exp\l 7 opexp: . exp '+' exp\l 8 imm: . '0'\l"]
|
|
13 -> 1 [style=solid label="\"if\""]
|
|
13 -> 2 [style=solid label="'0'"]
|
|
13 -> 15 [style=dashed label="exp"]
|
|
13 -> 4 [style=dashed label="ifexp"]
|
|
13 -> 5 [style=dashed label="opexp"]
|
|
13 -> 6 [style=dashed label="imm"]
|
|
14 [label="State 14\n\l 4 ifexp: \"if\" exp \"then\" exp elseexp .\l"]
|
|
14 -> "14R4" [style=solid]
|
|
"14R4" [label="R4", fillcolor=3, shape=diamond, style=filled]
|
|
15 [label="State 15\n\l 5 elseexp: \"else\" exp . [$end, \"then\", \"else\", '+']\l 7 opexp: exp . '+' exp\l"]
|
|
15 -> 9 [style=solid label="'+'"]
|
|
15 -> "15R5d" [label="['+']", style=solid]
|
|
"15R5d" [label="R5", fillcolor=5, shape=diamond, style=filled]
|
|
15 -> "15R5" [style=solid]
|
|
"15R5" [label="R5", fillcolor=3, shape=diamond, style=filled]
|
|
]])
|
|
|
|
m4_popdef([AT_TEST])
|
|
|
|
|
|
## -------------------------------- ##
|
|
## C++ Output File Prefix Mapping. ##
|
|
## -------------------------------- ##
|
|
|
|
AT_SETUP([C++ Output File Prefix Mapping])
|
|
|
|
# AT_TEST([PREFIX], [DIRECTIVES])
|
|
m4_pushdef([AT_TEST],
|
|
[AT_BISON_OPTION_PUSHDEFS([%skeleton "lalr1.cc" %define api.namespace {$1} $2])
|
|
AT_LOC_PUSHDEF([begin.line], [begin.column], [end.line], [end.column])
|
|
AT_DATA_GRAMMAR([$1.yy],
|
|
[[%skeleton "lalr1.cc"
|
|
%define api.namespace {$1}
|
|
$2
|
|
%code {
|
|
]AT_YYERROR_DECLARE[
|
|
]AT_YYLEX_DECLARE[
|
|
}
|
|
%%
|
|
exp: '0';
|
|
%%
|
|
]AT_YYERROR_DEFINE[
|
|
]AT_YYLEX_DEFINE(["0"])[
|
|
]])
|
|
|
|
AT_BISON_CHECK([-fcaret -o out/$1.cc -M out/=bar/ $1.yy])
|
|
AT_LANG_COMPILE([out/$1.o], [], [-Iout/include])
|
|
|
|
AT_LOC_POPDEF
|
|
AT_BISON_OPTION_POPDEFS
|
|
])
|
|
|
|
mkdir -p out/include/ast
|
|
|
|
AT_TEST([x1],
|
|
[%defines
|
|
%locations
|
|
%define api.location.file "include/ast/loc.hh"
|
|
])
|
|
|
|
# Check the CPP guard and Doxyen comments.
|
|
AT_CHECK([sed -ne 's/#line [0-9]\+ "/#line "/p;/INCLUDED/p;/\\file/{p;n;p;}' out/include/ast/loc.hh], [],
|
|
[[ ** \file bar/include/ast/loc.hh
|
|
** Define the x1::location class.
|
|
#ifndef YY_YY_BAR_INCLUDE_AST_LOC_HH_INCLUDED
|
|
# define YY_YY_BAR_INCLUDE_AST_LOC_HH_INCLUDED
|
|
#line "x1.yy"
|
|
#line "bar/include/ast/loc.hh"
|
|
#line "x1.yy"
|
|
#line "bar/include/ast/loc.hh"
|
|
#endif // !YY_YY_BAR_INCLUDE_AST_LOC_HH_INCLUDED
|
|
]])
|
|
|
|
AT_CHECK([sed -ne 's/^#line [0-9]\+ "/#line "/p;/INCLUDED/p;/\\file/{p;n;p;}' out/x1.hh], [],
|
|
[[ ** \file bar/x1.hh
|
|
** Define the x1::parser class.
|
|
#ifndef YY_YY_BAR_X1_HH_INCLUDED
|
|
# define YY_YY_BAR_X1_HH_INCLUDED
|
|
#line "x1.yy"
|
|
#line "bar/x1.hh"
|
|
#line "x1.yy"
|
|
#line "bar/x1.hh"
|
|
#endif // !YY_YY_BAR_X1_HH_INCLUDED
|
|
]])
|
|
|
|
AT_TEST([x2],
|
|
[%defines
|
|
%locations
|
|
%code requires {#include "include/ast/loc.hh"}
|
|
%define api.location.type {x1::location}])
|
|
|
|
m4_popdef([AT_TEST])
|
|
|
|
AT_DATA([main.cc],
|
|
[AT_DATA_SOURCE_PROLOGUE
|
|
[#include "x1.hh"
|
|
#include "x2.hh"
|
|
|
|
#define RUN(S) \
|
|
do { \
|
|
S::parser parser; \
|
|
int res = parser.parse(); \
|
|
if (res) \
|
|
std::cerr << #S": " << res << '\n'; \
|
|
} while (false)
|
|
|
|
int
|
|
main (void)
|
|
{
|
|
RUN(x1);
|
|
RUN(x2);
|
|
}
|
|
]])# main.cc
|
|
|
|
AT_COMPILE_CXX([parser], [[out/x[12].o main.cc]], [-Iout/])
|
|
AT_PARSER_CHECK([parser], [0])
|
|
|
|
AT_CLEANUP
|