| # Value type. -*- Autotest -*- | |
| # Copyright (C) 2013-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/>. | |
| 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([ | |
| AT_GLR2_CC_IF([AT_REQUIRE_CXX_STD(11, [echo "$at_std not supported"; continue])]) | |
| $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 %header, 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 %header], [$2], [$3], [$4], [$5], [$6], [$7]) | |
| ]) | |
| m4_foreach([b4_skel], [[yacc.c], [glr.c], [lalr1.cc], [glr.cc], [glr2.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 token names are not available directly in C++, we use their | |
| # codes 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 | |
| ]) |