Hello Akim,

I made the token constructors work. Doing this, I realized that the Symbol
constructors needed some changes, which I did in D.
After our discussion from Tuesday, I do agree that it's not the best
approach.
The code is quite unnecessarily complex, so I will rewrite the constructors
to be generated with M4, to be easier to maintain.

Now, the problem is that token constructors need the new version of the
Symbol constructors to be working correctly.
So it's your choice if you decide to install it like this or wait until I
rewrite the constructors.

It's a busy period for me right now, so I don't know if I have the time to
work Bison earlier than March 1st, but I will try my best.

Thank you again for the help with api.value.type union!

Adela



În sâm., 23 ian. 2021 la 15:57, Akim Demaille <[email protected]> a
scris:

> Hi Adela,
>
> > Le 23 janv. 2021 à 13:07, Adela Vais <[email protected]> a écrit :
> >
> >> It does not work because of compile-time issues in D.
> >
> > I will work on this over the weekend.
>
> Let me know if you need guidance for the remaining part.
From 53aa13dc7f0260fb6aacf9ed1c6147e8f882e680 Mon Sep 17 00:00:00 2001
From: Adela Vais <[email protected]>
Date: Mon, 25 Jan 2021 17:59:06 +0200
Subject: [PATCH for Dlang support 1/4] d: tests: various style fixes

Now the tests report values in yylex() directly, without storing them to the union first.

* tests/calc.at, tests/scanner.at: Here.
---
 tests/calc.at    | 54 +++++++++++++++++++++---------------------------
 tests/scanner.at | 13 ++++--------
 2 files changed, 28 insertions(+), 39 deletions(-)

diff --git a/tests/calc.at b/tests/calc.at
index 1d2e818d..f8dc660e 100644
--- a/tests/calc.at
+++ b/tests/calc.at
@@ -539,7 +539,7 @@ m4_define([AT_CALC_YYLEX(d)],
 import std.stdio;
 
 auto calcLexer(R)(R range)
-  if (isInputRange!R && is (ElementType!R : dchar))
+if (isInputRange!R && is (ElementType!R : dchar))
 {
   return new CalcLexer!R(range);
 }
@@ -556,17 +556,14 @@ auto calcLexer (File f)
 }
 
 class CalcLexer(R) : Lexer
-  if (isInputRange!R && is (ElementType!R : dchar))
+if (isInputRange!R && is (ElementType!R : dchar))
 {
   R input;
 
-  this(R r) {
-    input = r;
-  }
+  this(R r) { input = r; }
 
   ]AT_YYERROR_DEFINE[
-
-  Value value_;]AT_LOCATION_IF([[
+]AT_LOCATION_IF([[
   Location location;
 ]])[
   int parseInt ()
@@ -574,27 +571,27 @@ class CalcLexer(R) : Lexer
     auto res = 0;
     import std.uni : isNumber;
     while (input.front.isNumber)
-      {
-        res = res * 10 + (input.front - '0');]AT_LOCATION_IF([[
-        location.end.column += 1;]])[
-        input.popFront;
-      }
+    {
+      res = res * 10 + (input.front - '0');]AT_LOCATION_IF([[
+      location.end.column += 1;]])[
+      input.popFront;
+    }
     return res;
   }
 
   Symbol yylex ()
   {]AT_LOCATION_IF([[
-    location.begin = location.end;]])[
+    location.step();]])[
 
     import std.uni : isWhite, isNumber;
 
     // Skip initial spaces
     while (!input.empty && input.front != '\n' && isWhite (input.front))
-      {
-        input.popFront;]AT_LOCATION_IF([[
-        location.begin.column += 1;
-        location.end.column += 1;]])[
-      }
+    {
+      input.popFront;]AT_LOCATION_IF([[
+      location.end.column += 1;]])[
+    }]AT_LOCATION_IF([[
+    location.step();]])[
 
     // EOF.
     if (input.empty)
@@ -602,28 +599,25 @@ class CalcLexer(R) : Lexer
 
     // Numbers.
     if (input.front.isNumber)
-      {
-        value_.ival = parseInt;
-        return Symbol(TokenKind.]AT_TOKEN_PREFIX[NUM, value_.ival]AT_LOCATION_IF([[, location]])[);
-      }
+      return Symbol(TokenKind.]AT_TOKEN_PREFIX[NUM, parseInt]AT_LOCATION_IF([[, location]])[);
 
     // Individual characters
     auto c = input.front;]AT_LOCATION_IF([[
     if (c == '\n')
-      {
-        location.end.line += 1;
-        location.end.column = 1;
-      }
+    {
+      location.end.line += 1;
+      location.end.column = 1;
+    }
     else
       location.end.column += 1;]])[
     input.popFront;
 
     // An explicit error raised by the scanner. */
     if (c == '#')
-      {
-        stderr.writeln (]AT_LOCATION_IF([location, ": ", ])["syntax error: invalid character: '#'");
-        return Symbol(TokenKind.]AT_TOKEN_PREFIX[YYerror]AT_LOCATION_IF([[, location]])[);
-      }
+    {
+      stderr.writeln (]AT_LOCATION_IF([location, ": ", ])["syntax error: invalid character: '#'");
+      return Symbol(TokenKind.]AT_TOKEN_PREFIX[YYerror]AT_LOCATION_IF([[, location]])[);
+    }
 
     switch (c)
     {
diff --git a/tests/scanner.at b/tests/scanner.at
index 92f5a380..7f494f0d 100644
--- a/tests/scanner.at
+++ b/tests/scanner.at
@@ -94,7 +94,7 @@ m4_define([AT_RAW_YYLEX(d)],
 import std.stdio;
 
 auto yyLexer(R)(R range)
-  if (isInputRange!R && is (ElementType!R : dchar))
+if (isInputRange!R && is (ElementType!R : dchar))
 {
   return new YYLexer!R(range);
 }
@@ -105,18 +105,14 @@ auto yyLexer ()
 }
 
 class YYLexer(R) : Lexer
-  if (isInputRange!R && is (ElementType!R : dchar))
+if (isInputRange!R && is (ElementType!R : dchar))
 {
   R input;
 
-  this(R r) {
-    input = r;
-  }
+  this(R r) { input = r; }
 
   ]AT_YYERROR_DEFINE[
 
-  Value value_;
-
   Symbol yylex ()
   {
     import std.uni : isNumber;
@@ -131,8 +127,7 @@ class YYLexer(R) : Lexer
     switch (c)
     {
     case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
-      value_.val = c - '0';
-      return Symbol(TokenKind.NUM, value_.val);
+      return Symbol(TokenKind.NUM, c - '0');
     case '+': return Symbol(TokenKind.PLUS);
     case '-': return Symbol(TokenKind.MINUS);
     case '*': return Symbol(TokenKind.STAR);
-- 
2.17.1

From 6c826da72bfdbb9f5477f9c1e304481ea54a9dc7 Mon Sep 17 00:00:00 2001
From: Akim Demaille <[email protected]>
Date: Thu, 21 Jan 2021 21:45:45 +0100
Subject: [PATCH for Dlang support 3/4] d: add api.value.type union support

The union of the values is handled by the backend.
In D, unions can hold classes, structs, etc., so this is more similar
to the C++ api.value.type variant.

* data/skeletons/bison.m4 (b4_union_if): New.
* data/skeletons/d.m4, data/skeletons/lalr1.d: Here.
* tests/calc.at, tests/local.at: Test it.
---
 data/skeletons/bison.m4 |  12 ++++-
 data/skeletons/d.m4     | 116 ++++++++++++++++++++++++++++++++++++++--
 data/skeletons/lalr1.d  |   2 +-
 tests/calc.at           |  11 ++--
 tests/local.at          |   3 ++
 5 files changed, 133 insertions(+), 11 deletions(-)

diff --git a/data/skeletons/bison.m4 b/data/skeletons/bison.m4
index 739bbbfb..6be8b77c 100644
--- a/data/skeletons/bison.m4
+++ b/data/skeletons/bison.m4
@@ -1132,9 +1132,19 @@ m4_define([b4_parse_error_bmatch],
 
 
 
+# b4_union_if([IF-VARIANT-ARE-USED], [IF-NOT])
+# --------------------------------------------
+# Whether api.value.type=union is used.
+m4_define([b4_union_flag], [[0]])
+b4_percent_define_ifdef([[api.value.type]],
+   [m4_case(b4_percent_define_get_kind([[api.value.type]]), [keyword],
+            [m4_case(b4_percent_define_get([[api.value.type]]), [union],
+                    [m4_define([b4_union_flag], [[1]])])])])
+b4_define_flag_if([union])
+
+
 # b4_variant_if([IF-VARIANT-ARE-USED], [IF-NOT])
 # ----------------------------------------------
-b4_percent_define_if_define([variant])
 m4_define([b4_variant_flag], [[0]])
 b4_percent_define_ifdef([[api.value.type]],
    [m4_case(b4_percent_define_get_kind([[api.value.type]]), [keyword],
diff --git a/data/skeletons/d.m4 b/data/skeletons/d.m4
index f338e367..034cb436 100644
--- a/data/skeletons/d.m4
+++ b/data/skeletons/d.m4
@@ -214,8 +214,12 @@ m4_define([b4_symbol_translate],
 m4_define([_b4_constructor_maker_define_types],
 [b4_token_visible_if([$1],
   [b4_symbol_if([$1], [has_type],
-    [[            case TokenKind.]b4_symbol([$1], [id])[: value_.]b4_symbol([$1], [type])[ = val;
-                                break;]], [dnl])
+[[            ]dnl
+[static if (__traits(compiles, value_.]dnl
+b4_union_if([b4_symbol([$1], [id])], [b4_symbol([$1], [type])])[ = val))]
+[              ]dnl
+[case TokenKind.]b4_symbol([$1], [id])[: value_.]dnl
+b4_union_if([b4_symbol([$1], [id])], [b4_symbol([$1], [type])])[ = val; break;]], [dnl])
 ])])
 
 # _b4_token_maker_define_types(SYMBOL-NUM)
@@ -224,7 +228,8 @@ m4_define([_b4_constructor_maker_define_types],
 m4_define([_b4_token_maker_define_types],
 [b4_token_visible_if([$1],
   [b4_symbol_if([$1], [has_type],
-    [      typeof(YYSemanticType.]b4_symbol([$1], [type])[).stringof],
+    [      ]b4_union_if([["]b4_symbol([$1], [type])["]],
+                        [[typeof(YYSemanticType.]b4_symbol([$1], [type])[).stringof]]),
     [      "void"]),
 ])])
 
@@ -295,7 +300,7 @@ m4_define([b4_declare_symbol_enum],
 ]])
 
 
-# b4-case(ID, CODE, [COMMENTS])
+# b4_case(ID, CODE, [COMMENTS])
 # -----------------------------
 m4_define([b4_case], [    case $1:m4_ifval([$3], [ b4_comment([$3])])
 $2
@@ -322,6 +327,109 @@ m4_define([b4_location_type], b4_percent_define_ifdef([[location_type]],[b4_perc
 m4_define([b4_position_type], b4_percent_define_ifdef([[position_type]],[b4_percent_define_get([[position_type]])],[YYPosition]))
 
 
+## ---------------- ##
+## api.value.type.  ##
+## ---------------- ##
+
+
+# ---------------------- #
+# api.value.type=union.  #
+# ---------------------- #
+
+# b4_symbol_type_register(SYMBOL-NUM)
+# -----------------------------------
+# Symbol SYMBOL-NUM has a type (for union) instead of a type-tag.
+# Extend the definition of %union's body (b4_union_members) with a
+# field of that type, and extend the symbol's "type" field to point to
+# the field name, instead of the type name.
+m4_define([b4_symbol_type_register],
+[m4_define([b4_symbol($1, type_tag)],
+           [b4_symbol_if([$1], [has_id],
+                         [b4_symbol([$1], [id])],
+                         [yykind_[]b4_symbol([$1], [number])])])dnl
+m4_append([b4_union_members],
+m4_expand([m4_format([  %-40s %s],
+                     m4_expand([b4_symbol([$1], [type]) b4_symbol([$1], [type_tag]);]),
+                     [b4_symbol_tag_comment([$1])])]))
+])
+
+
+# b4_type_define_tag(SYMBOL1-NUM, ...)
+# ------------------------------------
+# For the batch of symbols SYMBOL1-NUM... (which all have the same
+# type), enhance the %union definition for each of them, and set
+# there "type" field to the field tag name, instead of the type name.
+m4_define([b4_type_define_tag],
+[b4_symbol_if([$1], [has_type],
+              [m4_map([b4_symbol_type_register], [$@])])
+])
+
+
+# b4_symbol_value_union(VAL, SYMBOL-NUM, [TYPE])
+# ----------------------------------------------
+# Same of b4_symbol_value, but when api.value.type=union.
+m4_define([b4_symbol_value_union],
+[m4_ifval([$3],
+          [(*($3*)(&$1))],
+          [m4_ifval([$2],
+                    [b4_symbol_if([$2], [has_type],
+                                  [($1.b4_symbol([$2], [type_tag]))],
+                                  [$1])],
+                    [$1])])])
+
+
+# b4_value_type_setup_union
+# -------------------------
+# Setup support for api.value.type=union.  Symbols are defined with a
+# type instead of a union member name: build the corresponding union,
+# and give the symbols their tag.
+m4_define([b4_value_type_setup_union],
+[m4_define([b4_union_members])
+b4_type_foreach([b4_type_define_tag])
+m4_copy_force([b4_symbol_value_union], [b4_symbol_value])
+])
+
+
+# _b4_value_type_setup_keyword
+# ----------------------------
+# api.value.type is defined with a keyword/string syntax.  Check if
+# that is properly defined, and prepare its use.
+m4_define([_b4_value_type_setup_keyword],
+[b4_percent_define_check_values([[[[api.value.type]],
+                                  [[none]],
+                                  [[union]],
+                                  [[union-directive]],
+                                  [[yystype]]]])dnl
+m4_case(b4_percent_define_get([[api.value.type]]),
+        [union],   [b4_value_type_setup_union])])
+
+
+# b4_value_type_setup
+# -------------------
+# Check if api.value.type is properly defined, and possibly prepare
+# its use.
+b4_define_silent([b4_value_type_setup],
+[
+# Define default value.
+b4_percent_define_ifdef([[api.value.type]], [],
+[# %union => api.value.type=union-directive
+m4_ifdef([b4_union_members],
+[m4_define([b4_percent_define_kind(api.value.type)], [keyword])
+m4_define([b4_percent_define(api.value.type)], [union-directive])],
+[# no tag seen => api.value.type={int}
+m4_if(b4_tag_seen_flag, 0,
+[m4_define([b4_percent_define_kind(api.value.type)], [code])
+m4_define([b4_percent_define(api.value.type)], [int])],
+[# otherwise api.value.type=yystype
+m4_define([b4_percent_define_kind(api.value.type)], [keyword])
+m4_define([b4_percent_define(api.value.type)], [yystype])])])])
+
+# Set up.
+m4_bmatch(b4_percent_define_get_kind([[api.value.type]]),
+   [keyword], [_b4_value_type_setup_keyword])
+])
+
+
 ## ----------------- ##
 ## Semantic Values.  ##
 ## ----------------- ##
diff --git a/data/skeletons/lalr1.d b/data/skeletons/lalr1.d
index 62b12d1c..889e0b47 100644
--- a/data/skeletons/lalr1.d
+++ b/data/skeletons/lalr1.d
@@ -206,7 +206,7 @@ public struct ]b4_location_type[
 
 private immutable bool yy_location_is_class = false;
 
-]])])m4_ifdef([b4_user_union_members], [private union YYSemanticType
+]])])[]b4_value_type_setup[]m4_ifdef([b4_user_union_members], [private union YYSemanticType
 {
 b4_user_union_members
 };],
diff --git a/tests/calc.at b/tests/calc.at
index f8dc660e..dacaae64 100644
--- a/tests/calc.at
+++ b/tests/calc.at
@@ -645,11 +645,11 @@ m4_define([_AT_DATA_CALC_Y(d)],
   alias semantic_value = int;
 }
 /* Exercise %union. */
-%union
+]AT_UNION_IF([[]], [[%union
 {
   semantic_value ival;
-};
-%printer { yyo.write($$); } <ival>;
+};]])[
+%printer { yyo.write($$); } <]AT_UNION_IF([[int]], [[ival]])[>;
 
 %code {
 ]AT_TOKEN_TRANSLATE_IF([[
@@ -670,8 +670,8 @@ m4_define([_AT_DATA_CALC_Y(d)],
 
 /* Bison Declarations */
 %token EOF 0 ]AT_TOKEN_TRANSLATE_IF([_("end of file")], ["end of input"])[
-%token <ival> NUM   "number"
-%type  <ival> exp
+%token <]AT_UNION_IF([[int]], [[ival]])[> NUM   "number"
+%type  <]AT_UNION_IF([[int]], [[ival]])[> exp
 
 %token EQUAL  "="
        MINUS  "-"
@@ -1499,6 +1499,7 @@ AT_CHECK_CALC_LALR1_D([%locations %define parse.lac full %define parse.error det
 #AT_CHECK_CALC_LALR1_D([%locations %define parse.error detailed %debug %verbose %parse-param {semantic_value *result}{int *count}{int *nerrs}])
 #AT_CHECK_CALC_LALR1_D([%locations %define parse.error detailed %debug %define api.prefix {calc} %verbose %parse-param {semantic_value *result}{int *count}{int *nerrs}])
 
+AT_CHECK_CALC_LALR1_D([%define parse.error custom %define api.value.type union])
 
 # ----------------------- #
 # LALR1 Java Calculator.  #
diff --git a/tests/local.at b/tests/local.at
index 77112b5e..89fb5d53 100644
--- a/tests/local.at
+++ b/tests/local.at
@@ -317,6 +317,8 @@ m4_pushdef([AT_TOKEN_RAW_IF],
 [m4_bmatch([$3], [%define api\.token\.raw], [$1], [$2])])
 m4_pushdef([AT_VARIANT_IF],
 [m4_bmatch([$3], [%define api\.value\.type variant], [$1], [$2])])
+m4_pushdef([AT_UNION_IF],
+[m4_bmatch([$3], [%define api\.value\.type union], [$1], [$2])])
 m4_pushdef([AT_API_prefix],
 [m4_bmatch([$3], [%define api\.prefix {.*}],
            [m4_bregexp([$3], [%define api\.prefix {\([^\}]*\)}], [\1])],
@@ -440,6 +442,7 @@ m4_popdef([AT_YYERROR_ARG_LOC_IF])
 m4_popdef([AT_API_PREFIX])
 m4_popdef([AT_API_prefix])
 m4_popdef([AT_VARIANT_IF])
+m4_popdef([AT_UNION_IF])
 m4_popdef([AT_TOKEN_RAW_IF])
 m4_popdef([AT_TOKEN_PREFIX])
 m4_popdef([AT_TOKEN_CTOR_IF])
-- 
2.17.1

From 9e61237639d839c49e9b535bfc2ac93048591180 Mon Sep 17 00:00:00 2001
From: Adela Vais <[email protected]>
Date: Mon, 25 Jan 2021 20:04:39 +0200
Subject: [PATCH for Dlang support 2/4] d: fix the Symbol constructor

Now, the Symbol constructor stores the value in the union field corresponding
to the TokenKind.

* data/skeletons/d.m4 (visibleTokenTypes, _b4_constructor_maker_define_types,
_b4_token_maker_define_types): New.
---
 data/skeletons/d.m4 | 46 ++++++++++++++++++++++++++++++++++++++++-----
 1 file changed, 41 insertions(+), 5 deletions(-)

diff --git a/data/skeletons/d.m4 b/data/skeletons/d.m4
index 4b5f9f95..f338e367 100644
--- a/data/skeletons/d.m4
+++ b/data/skeletons/d.m4
@@ -208,6 +208,26 @@ b4_symbol_foreach([b4_token_enum])dnl
 m4_define([b4_symbol_translate],
 [[_($1)]])
 
+# _b4_constructor_maker_define_types(SYMBOL-NUM)
+# ----------------------------------
+# Declare the case branches in the Symbol constructor for SYMBOL-NUM values.
+m4_define([_b4_constructor_maker_define_types],
+[b4_token_visible_if([$1],
+  [b4_symbol_if([$1], [has_type],
+    [[            case TokenKind.]b4_symbol([$1], [id])[: value_.]b4_symbol([$1], [type])[ = val;
+                                break;]], [dnl])
+])])
+
+# _b4_token_maker_define_types(SYMBOL-NUM)
+# ----------------------------------
+# Declare the types for SYMBOL-NUM values.
+m4_define([_b4_token_maker_define_types],
+[b4_token_visible_if([$1],
+  [b4_symbol_if([$1], [has_type],
+    [      typeof(YYSemanticType.]b4_symbol([$1], [type])[).stringof],
+    [      "void"]),
+])])
+
 ## -------------- ##
 ## Symbol kinds.  ##
 ## -------------- ##
@@ -467,13 +487,29 @@ m4_define([b4_symbol_type_define],
       kind = yytranslate_(token);]b4_locations_if([
       location_ = loc;])[
     }
-    static foreach (member; __traits(allMembers, YYSemanticType))
+
+    // The types of TokenKinds' corresponding semantic values.
+    private static immutable string[] visibleTokenTypes = @{
+]m4_text_wrap(m4_expand([b4_symbol_foreach([_b4_token_maker_define_types])]),
+[    ], [    ])[@};
+
+    // Avoid duplicate constructors by using an associative array.
+    import std.array : assocArray;
+    import std.range : repeat;
+    static foreach (type, _; assocArray(visibleTokenTypes, true.repeat))
     {
-      this(TokenKind token, typeof(mixin("YYSemanticType." ~ member)) val]b4_locations_if([[, Location loc]])[)
+      static if (type != "void")
       {
-        kind = yytranslate_(token);
-        mixin("value_." ~ member ~ " = val;");]b4_locations_if([
-        location_ = loc;])[
+        this(TokenKind token, mixin(type) val][]b4_locations_if([[, Location loc]])[)
+        {
+          kind = yytranslate_(token);
+          switch (token)
+          {
+]b4_symbol_foreach([_b4_constructor_maker_define_types])[
+            default: assert(0);
+          }]b4_locations_if([
+          location_ = loc;])[
+        }
       }
     }
     SymbolKind token() { return kind; }
-- 
2.17.1

From d4e228095c3c2021cabda093a45712d8f7892352 Mon Sep 17 00:00:00 2001
From: Adela Vais <[email protected]>
Date: Mon, 25 Jan 2021 22:53:30 +0200
Subject: [PATCH for Dlang support 4/4] d: add token constructors support

The user can return from yylex() by calling the Symbol method of the same name as
the TokenKind reported, and adding the parameters for value and location if necessary.
These methods generate compile-time errors if the parameters are not correlated.
Token constructors work with both %union and api.value.type union.

* data/skeletons/d.m4: Here.
* tests/calc.at: Test it.
---
 data/skeletons/d.m4 | 32 ++++++++++++++++++++++++++++++++
 tests/calc.at       | 41 +++++++++++++++++++++++++----------------
 2 files changed, 57 insertions(+), 16 deletions(-)

diff --git a/data/skeletons/d.m4 b/data/skeletons/d.m4
index 034cb436..6b911ce2 100644
--- a/data/skeletons/d.m4
+++ b/data/skeletons/d.m4
@@ -233,6 +233,37 @@ m4_define([_b4_token_maker_define_types],
     [      "void"]),
 ])])
 
+# b4_token_constructor_define
+# ---------------------------
+# Define the overloaded versions of make_symbol for all the value types.
+m4_define([b4_token_constructor_define],
+[[
+    /* Implementation of token constructors for each symbol type visible to
+     * the user. The visibleTokenTypes array provides the types.
+     * The code generates static methods that have the same names as the TokenKinds.
+     */
+    static foreach (member; __traits(allMembers, TokenKind))
+    {
+      static if (mixin("TokenKind." ~ member) >= 0)
+      {
+        static if (visibleTokenTypes[mixin("TokenKind." ~ member)] == "void")
+        {
+          mixin("static auto " ~ member ~ " (]b4_locations_if([[Location l]])[)
+          {
+            return Symbol(TokenKind." ~ member ~ "]b4_locations_if([[, l]])[);
+          }");
+        }
+        else
+        {
+          mixin("static auto " ~ member ~ "(" ~
+            visibleTokenTypes[mixin("TokenKind." ~ member)] ~" v]b4_locations_if([[, Location l]])[)
+          {
+            return Symbol(TokenKind." ~ member ~ ", v]b4_locations_if([[, l]])[);
+          }");
+        }
+      }
+    }]])
+
 ## -------------- ##
 ## Symbol kinds.  ##
 ## -------------- ##
@@ -623,5 +654,6 @@ m4_define([b4_symbol_type_define],
     SymbolKind token() { return kind; }
     Value value() { return value_; }]b4_locations_if([[
     Location location() { return location_; }]])[
+]b4_token_ctor_if([b4_token_constructor_define])[
   }
 ]])
diff --git a/tests/calc.at b/tests/calc.at
index dacaae64..e0eef395 100644
--- a/tests/calc.at
+++ b/tests/calc.at
@@ -519,6 +519,15 @@ m4_copy([_AT_DATA_CALC_Y(c)], [_AT_DATA_CALC_Y(c++)])
 ## Calc in D.  ##
 ## ----------- ##
 
+# AT_YYLEX_RETURN_VAL
+# ---------------
+m4_define([AT_YYLEX_RETURN_VAL],
+[return dnl
+AT_TOKEN_CTOR_IF(
+  [[Symbol.]AT_TOKEN_PREFIX[$1](m4_ifval([$2], [$2])[]AT_LOCATION_IF([m4_ifval([$2], [, ])[location]])[);]],
+  [[Symbol(TokenKind.]AT_TOKEN_PREFIX[$1]m4_ifval([$2], [, $2])[]AT_LOCATION_IF([[, location]])[);]])]
+)
+
 # AT_CALC_MAIN(d).
 m4_define([AT_CALC_MAIN(d)],
 [[int main (string[] args)
@@ -595,11 +604,10 @@ if (isInputRange!R && is (ElementType!R : dchar))
 
     // EOF.
     if (input.empty)
-      return Symbol(TokenKind.]AT_TOKEN_PREFIX[EOF]AT_LOCATION_IF([[, location]])[);
-
+      ]AT_YYLEX_RETURN_VAL([EOF])[
     // Numbers.
     if (input.front.isNumber)
-      return Symbol(TokenKind.]AT_TOKEN_PREFIX[NUM, parseInt]AT_LOCATION_IF([[, location]])[);
+      ]AT_YYLEX_RETURN_VAL([NUM], [parseInt])[
 
     // Individual characters
     auto c = input.front;]AT_LOCATION_IF([[
@@ -616,22 +624,22 @@ if (isInputRange!R && is (ElementType!R : dchar))
     if (c == '#')
     {
       stderr.writeln (]AT_LOCATION_IF([location, ": ", ])["syntax error: invalid character: '#'");
-      return Symbol(TokenKind.]AT_TOKEN_PREFIX[YYerror]AT_LOCATION_IF([[, location]])[);
+      ]AT_YYLEX_RETURN_VAL([YYerror])[
     }
 
     switch (c)
     {
-      case '+':  return Symbol(TokenKind.]AT_TOKEN_PREFIX[PLUS]AT_LOCATION_IF([[, location]])[);
-      case '-':  return Symbol(TokenKind.]AT_TOKEN_PREFIX[MINUS]AT_LOCATION_IF([[, location]])[);
-      case '*':  return Symbol(TokenKind.]AT_TOKEN_PREFIX[STAR]AT_LOCATION_IF([[, location]])[);
-      case '/':  return Symbol(TokenKind.]AT_TOKEN_PREFIX[SLASH]AT_LOCATION_IF([[, location]])[);
-      case '(':  return Symbol(TokenKind.]AT_TOKEN_PREFIX[LPAR]AT_LOCATION_IF([[, location]])[);
-      case ')':  return Symbol(TokenKind.]AT_TOKEN_PREFIX[RPAR]AT_LOCATION_IF([[, location]])[);
-      case '\n': return Symbol(TokenKind.]AT_TOKEN_PREFIX[EOL]AT_LOCATION_IF([[, location]])[);
-      case '=':  return Symbol(TokenKind.]AT_TOKEN_PREFIX[EQUAL]AT_LOCATION_IF([[, location]])[);
-      case '^':  return Symbol(TokenKind.]AT_TOKEN_PREFIX[POW]AT_LOCATION_IF([[, location]])[);
-      case '!':  return Symbol(TokenKind.]AT_TOKEN_PREFIX[NOT]AT_LOCATION_IF([[, location]])[);
-      default:   return Symbol(TokenKind.]AT_TOKEN_PREFIX[YYUNDEF]AT_LOCATION_IF([[, location]])[);
+      case '+':  ]AT_YYLEX_RETURN_VAL([PLUS])[
+      case '-':  ]AT_YYLEX_RETURN_VAL([MINUS])[
+      case '*':  ]AT_YYLEX_RETURN_VAL([STAR])[
+      case '/':  ]AT_YYLEX_RETURN_VAL([SLASH])[
+      case '(':  ]AT_YYLEX_RETURN_VAL([LPAR])[
+      case ')':  ]AT_YYLEX_RETURN_VAL([RPAR])[
+      case '\n': ]AT_YYLEX_RETURN_VAL([EOL])[
+      case '=':  ]AT_YYLEX_RETURN_VAL([EQUAL])[
+      case '^':  ]AT_YYLEX_RETURN_VAL([POW])[
+      case '!':  ]AT_YYLEX_RETURN_VAL([NOT])[
+      default:   ]AT_YYLEX_RETURN_VAL([YYUNDEF])[
     }
   }
 }
@@ -1499,7 +1507,8 @@ AT_CHECK_CALC_LALR1_D([%locations %define parse.lac full %define parse.error det
 #AT_CHECK_CALC_LALR1_D([%locations %define parse.error detailed %debug %verbose %parse-param {semantic_value *result}{int *count}{int *nerrs}])
 #AT_CHECK_CALC_LALR1_D([%locations %define parse.error detailed %debug %define api.prefix {calc} %verbose %parse-param {semantic_value *result}{int *count}{int *nerrs}])
 
-AT_CHECK_CALC_LALR1_D([%define parse.error custom %define api.value.type union])
+AT_CHECK_CALC_LALR1_D([%define api.token.constructor %locations %define parse.error custom %define api.value.type union])
+AT_CHECK_CALC_LALR1_D([%define api.token.constructor %locations %define parse.error detailed])
 
 # ----------------------- #
 # LALR1 Java Calculator.  #
-- 
2.17.1

Reply via email to