| # Value type. -*- Autotest -*- |
| |
| # Copyright (C) 2013-2015, 2018-2019 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([[Value type tests.]]) |
| |
| |
| ## ----------------------------------- ## |
| ## %union vs. %define api.value.type. ## |
| ## ----------------------------------- ## |
| |
| AT_SETUP([[%union vs. %define api.value.type]]) |
| |
| AT_DATA([[input.y]], |
| [[%union { int ival; } |
| %define api.value.type union-directive |
| %% |
| exp: %empty; |
| ]]) |
| |
| AT_BISON_CHECK([[input.y]], [[1]], [[]], |
| [[input.y:2.1-38: error: '%union' and '%define api.value.type' cannot be used together |
| ]]) |
| |
| AT_CLEANUP |
| |
| ## ---------------------------------------- ## |
| ## %yacc vs. %define api.value.type union. ## |
| ## ---------------------------------------- ## |
| |
| AT_SETUP([[%yacc vs. %define api.value.type union]]) |
| |
| AT_DATA([[input.y]], |
| [[%yacc |
| %define api.value.type union |
| %% |
| exp: %empty; |
| ]]) |
| |
| AT_BISON_CHECK([[input.y]], [[1]], [[]], |
| [[input.y:2.1-28: error: '%yacc' and '%define api.value.type "union"' cannot be used together |
| ]]) |
| |
| AT_CLEANUP |
| |
| |
| ## ---------------- ## |
| ## api.value.type. ## |
| ## ---------------- ## |
| |
| # _AT_TEST($1: BISON-DIRECTIVES, |
| # $2: MORE-BISON-DIRECTIVES, |
| # $3: PARSER-ACTION, |
| # $4: INPUT, |
| # $5: SCANNER-ACTION, |
| # $6: RESULT, |
| # $7: REQUIREMENT) |
| # -------------------------------------- |
| # Compile the grammar and check the expected result. |
| # BISON-DIRECTIVES are passed to AT_SETUP, contrary to MORE-BISON-DIRECTIVES. |
| # |
| # Use REQUIREMENT e.g. skip the test if the compiler does not meet the |
| # requirements. |
| m4_pushdef([_AT_TEST], |
| [ |
| AT_SETUP([$1]) |
| AT_KEYWORDS([api.value.type]) |
| |
| AT_BISON_OPTION_PUSHDEFS([%debug $1 $2]) |
| AT_DATA_GRAMMAR([test.y], |
| [[%debug |
| |
| %code |
| { |
| # include <stdio.h> |
| # include <stdlib.h> |
| ]AT_YYERROR_DECLARE[ |
| ]AT_YYLEX_DECLARE[ |
| } |
| |
| ]$1[ |
| ]$2[ |
| |
| %% |
| |
| start: $3; |
| |
| %% |
| ]AT_YYERROR_DEFINE[ |
| ]AT_YYLEX_DEFINE([$4], [$5])[ |
| ]AT_MAIN_DEFINE[ |
| ]]) |
| |
| AT_LANG_FOR_EACH_STD([ |
| $7 |
| AT_FULL_COMPILE([[test]]) |
| AT_PARSER_CHECK([[test]], 0, [[$6 |
| ]]) |
| ]) |
| |
| AT_BISON_OPTION_POPDEFS |
| AT_CLEANUP |
| ]) |
| |
| # AT_TEST($1: BISON-DIRECTIVES, |
| # $2: MORE-BISON-DIRECTIVES, |
| # $3: PARSER-ACTION, |
| # $4: INPUT, |
| # $5: SCANNER-ACTION, |
| # $6: RESULT, |
| # $7: REQUIREMENT) |
| # -------------------------------------- |
| # Check with and without %defines, to avoid regressions. It turns out |
| # that in that case yacc.c calls the set-up of the %union twice, |
| # because YYSTYPE is defined once in the header, and once in the |
| # implementation file (eventually it'd be better to include the header |
| # file, but that's another story). Unfortunately running these macros |
| # a second time doubled the side-effects and resulted in a double |
| # definition of the union members. |
| m4_pushdef([AT_TEST], |
| [_AT_TEST([$1], [$2], [$3], [$4], [$5], [$6], [$7]) |
| _AT_TEST([$1 %defines], [$2], [$3], [$4], [$5], [$6], [$7]) |
| ]) |
| |
| m4_foreach([b4_skel], [[yacc.c], [glr.c], [lalr1.cc], [glr.cc]], |
| [# A built-in type. |
| AT_TEST([%skeleton "]b4_skel[" |
| %define api.value.type {double}], |
| [], |
| ['1' '2' { printf ("%2.1f\n", $1 + $2); }], |
| ["12"], |
| [if (res) |
| AT_VAL = (res - '0') / 10.0], |
| [0.3]) |
| |
| # A typedef which looks like a Bison keyword, but it's using braces. |
| AT_TEST([%skeleton "]b4_skel[" |
| %define api.value.type {variant}], |
| [%code requires { typedef double variant; }], |
| ['1' '2' { printf ("%2.1f\n", $1 + $2); }], |
| ["12"], |
| [if (res) |
| AT_VAL = (res - '0') / 10.0], |
| [0.3]) |
| |
| # A user defined struct. |
| AT_TEST([%skeleton "]b4_skel[" |
| %define api.value.type {struct foo}], |
| [%code requires { struct foo { float fval; int ival; }; }], |
| ['1' '2' |
| { printf ("%d %2.1f\n", $1.ival + $2.ival, $1.fval + $2.fval); }], |
| ["12"], |
| [if (res) |
| { |
| AT_VAL.ival = (res - '0') * 10; |
| AT_VAL.fval = YY_CAST (float, res - '0') / 10.f; |
| }], |
| [30 0.3]) |
| |
| # A user defined struct that uses pointers. |
| AT_TEST([%skeleton "]b4_skel[" |
| %define api.value.type {struct bar}], |
| [%code requires |
| { |
| struct u |
| { |
| int ival; |
| }; |
| struct bar |
| { |
| struct u *up; |
| }; |
| } |
| %token <up->ival> '1' '2' |
| %printer { ]AT_CXX_IF([[yyo << $$]], |
| [[fprintf (yyo, "%d", $$)]])[; } <up->ival> |
| ], |
| ['1' '2' |
| { |
| printf ("%d %d\n", $1, $<up->ival>2); |
| free ($<up>1); |
| free ($<up>2); |
| }], |
| ["12"], |
| [if (res) |
| { |
| AT_VAL.up = YY_CAST (struct u *, malloc (sizeof *AT_VAL.up)); |
| assert (AT_VAL.up); |
| AT_VAL.up->ival = res - '0'; |
| } |
| ], |
| [1 2]) |
| |
| # A user defined union. |
| AT_TEST([%skeleton "]b4_skel[" |
| %define api.value.type {union foo}], |
| [%code requires { union foo { float fval; int ival; }; }], |
| ['1' '2' { printf ("%d %2.1f\n", $1.ival, $2.fval); }], |
| ["12"], |
| [if (res == '1') |
| AT_VAL.ival = 10; |
| else if (res == '2') |
| AT_VAL.fval = .2f], |
| [10 0.2]) |
| |
| # A %union and a named %union. In C++ named %union is an error. |
| m4_foreach([b4_union], |
| [m4_bmatch(b4_skel, [\.cc$], |
| [[%union]], |
| [[%union], [%union foo], |
| [%define api.value.union.name foo; %union]])], |
| [AT_TEST([%skeleton "]b4_skel[" |
| ]b4_union[ { float fval; int ival; };], |
| [%token <ival> '1'; |
| %token <fval> '2';], |
| ['1' '2' { printf ("%d %2.1f\n", $1, $2); }], |
| ["12"], |
| [if (res == '1') |
| AT_VAL.ival = 10; |
| else if (res == '2') |
| AT_VAL.fval = 0.2f], |
| [10 0.2])]) |
| |
| # A Bison-defined union. |
| # The tokens names are not available directly in C++, we use their |
| # user number to keep it simple between C and C++. |
| AT_TEST([%skeleton "]b4_skel[" |
| %define api.value.type union], |
| [%token <int> ONE 101; |
| %token <float> TWO 102 THREE 103; |
| %printer { ]AT_CXX_IF([[yyo << $$]], |
| [[fprintf (yyo, "%d", $$)]])[; } <int> |
| %printer { ]AT_CXX_IF([[yyo << $$]], |
| [[fprintf (yyo, "%f", $$)]])[; } <float> |
| ], |
| [ONE TWO THREE { printf ("%d %2.1f %2.1f\n", $1, $2, $3); }], |
| [{ 101, 102, 103, EOF }], |
| [if (res == 101) |
| AT_VAL.ONE = 10; |
| else if (res == 102) |
| AT_VAL.TWO = .2f; |
| else if (res == 103) |
| AT_VAL.THREE = 3.3f], |
| [10 0.2 3.3]) |
| |
| # A Bison-defined variant, for lalr1.cc only. |
| m4_if(b4_skel, [lalr1.cc], [ |
| AT_TEST([%skeleton "]b4_skel[" |
| %define api.value.type variant], |
| [%token <int> '1'; |
| %token <std::string> '2';], |
| ['1' '2' { std::cout << $1 << ", " << $2 << '\n'; }], |
| ["12"], |
| [if (res == '1') |
| AT_VAL.build (10); |
| else if (res == '2') |
| AT_VAL.build<std::string> ("two");], |
| [10, two]) |
| |
| # Test a regression where we passed user types (we can include |
| # commas) to a CPP macro. |
| AT_TEST([%skeleton "]b4_skel[" |
| %define api.value.type variant], |
| [%token <std::pair<int, int>> '1'; |
| %token <std::pair<std::string, std::string>> '2';], |
| ['1' '2' |
| { |
| std::cout << $1.first << ':' << $1.second << ", " |
| << $2.first << ':' << $2.second << '\n'; |
| }], |
| ["12"], |
| [if (res == '1') |
| AT_VAL.build (std::make_pair (10, 11)); |
| else if (res == '2') |
| AT_VAL.build (std::pair<std::string, std::string> ("two", "deux"));], |
| [10:11, two:deux]) |
| |
| # Type-based token constructors on move-only types, and types with commas. |
| AT_TEST([%skeleton "]b4_skel[" |
| %define api.value.type variant |
| %define api.token.constructor], |
| [[%token <std::pair<int, int>> '1' '2';]], |
| ['1' '2' |
| { |
| std::cout << $1.first << ':' << $1.second << ", " |
| << $2.first << ':' << $2.second << '\n'; |
| }], |
| ["12"], |
| [[typedef yy::parser::symbol_type symbol; |
| if (res) |
| return symbol (res, std::make_pair (res - '0', res - '0' + 1)); |
| else |
| return symbol (res)]], |
| [1:2, 2:3]) |
| |
| # Move-only types, and variadic emplace. |
| AT_TEST([%skeleton "]b4_skel[" |
| %code requires { #include <memory> } |
| %define api.value.type variant], |
| [[%token <std::unique_ptr<int>> '1'; |
| %token <std::pair<int, int>> '2';]], |
| ['1' '2' { std::cout << *$1 << ", " |
| << $2.first << ':' << $2.second << '\n'; }], |
| ["12"], |
| [[if (res == '1') |
| ]AT_VAL[.emplace <std::unique_ptr<int>> |
| (std::make_unique <int> (10)); |
| else if (res == '2') |
| ]AT_VAL[.emplace <std::pair<int, int>> (21, 22);]], |
| [10, 21:22], |
| [AT_REQUIRE_CXX_STD(14, [echo "$at_std not supported"; continue])]) |
| |
| # Token constructors on move-only types, and types with commas. |
| AT_TEST([%skeleton "]b4_skel[" |
| %code requires { #include <memory> } |
| %define api.value.type variant |
| %define api.token.constructor], |
| [[%token <std::unique_ptr<int>> ONE; |
| %token <std::pair<int, int>> TWO; |
| %token EOI 0;]], |
| [ONE TWO { std::cout << *$1 << ", " |
| << $2.first << ':' << $2.second << '\n'; }], |
| ["12"], |
| [[if (res == '1') |
| return yy::parser::make_ONE (std::make_unique<int> (10)); |
| else if (res == '2') |
| return yy::parser::make_TWO (std::make_pair (21, 22)); |
| else |
| return yy::parser::make_EOI ()]], |
| [10, 21:22], |
| [AT_REQUIRE_CXX_STD(14, [echo "$at_std not supported"; continue])]) |
| |
| # Type-based token constructors on move-only types, and types with commas. |
| AT_TEST([%skeleton "]b4_skel[" |
| %code requires { #include <memory> } |
| %define api.value.type variant |
| %define api.token.constructor], |
| [[%token <std::unique_ptr<int>> '1'; |
| %token <std::pair<int, int>> '2';]], |
| ['1' '2' { std::cout << *$1 << ", " |
| << $2.first << ':' << $2.second << '\n'; }], |
| ["12"], |
| [[if (res == '1') |
| return {res, std::make_unique<int> (10)}; |
| else if (res == '2') |
| return {res, std::make_pair (21, 22)}; |
| else |
| return res]], |
| [10, 21:22], |
| [AT_REQUIRE_CXX_STD(14, [echo "$at_std not supported"; continue])]) |
| |
| ]) |
| ]) |
| |
| m4_popdef([AT_TEST]) |
| m4_popdef([_AT_TEST]) |
| |
| |
| ## ------------------- ## |
| ## C++: Named %union. ## |
| ## ------------------- ## |
| |
| m4_foreach([b4_skel], [[lalr1.cc], [glr.cc]], |
| [AT_SETUP([b4_skel: Named %union]) |
| AT_DATA([input.y], |
| [%skeleton "]b4_skel[" |
| %union foo { float fval; int ival; }; |
| %% |
| exp: %empty; |
| ]) |
| AT_BISON_CHECK([input.y], 1, [], |
| [[input.y:2.8-10: error: named %union is invalid in C++ |
| ]]) |
| AT_CLEANUP |
| ]) |