From 5e3b8438d1e6c8377ca61965450b10543256df01 Mon Sep 17 00:00:00 2001
From: Adela Vais <adela.vais99@gmail.com>
Date: Thu, 14 Jan 2021 01:21:26 +0200
Subject: [PATCH for Dlang support 1/2] d: create token constructors

Symbol's constructor does not check if the TokenKind and the value it receives
correspond. The token constructors are methods of Symbol struct that can
make this check at compile time.

* data/skeletons/d.m4: Here.
* tests/calc.at: Test them.
---
 data/skeletons/d.m4 | 55 +++++++++++++++++++++++++++++++++++++++++++++
 tests/calc.at       | 42 ++++++++++++++++++++++------------
 2 files changed, 83 insertions(+), 14 deletions(-)

diff --git a/data/skeletons/d.m4 b/data/skeletons/d.m4
index 4b5f9f95..0e5b0bda 100644
--- a/data/skeletons/d.m4
+++ b/data/skeletons/d.m4
@@ -201,6 +201,7 @@ b4_symbol_foreach([b4_token_enum])dnl
 }
 ])
 
+
 # b4_symbol_translate(STRING)
 # ---------------------------
 # Used by "bison" in the array of symbol names to mark those that
@@ -208,6 +209,59 @@ b4_symbol_foreach([b4_token_enum])dnl
 m4_define([b4_symbol_translate],
 [[_($1)]])
 
+
+# _b4_token_maker_define_types(SYMBOL-NUM)
+# ----------------------------------
+# Declare the union types for SYMBOL-NUM values.
+m4_define([_b4_token_maker_define_types],
+[b4_token_visible_if([$1],
+  [b4_symbol_if([$1], [has_type],
+    [      "][b4_symbol([$1], [type])]["],
+    [      "void"]),
+])])
+
+
+# b4_token_constructor_define
+# ---------------------------
+# Define the overloaded versions of make_symbol for all the value types.
+m4_define([b4_token_constructor_define],
+[[    immutable string[] visibleTokenTypes = @{
+]b4_symbol_foreach([_b4_token_maker_define_types])[    @};
+    /* 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 names as the TokenKinds. */
+    static foreach (member; __traits(allMembers, TokenKind))
+    {
+      static if (mixin("TokenKind." ~ member) >= 0)
+      {
+        static if (visibleTokenTypes[mixin("TokenKind." ~ member)] == "void")
+        {]b4_locations_if([[
+          mixin("static auto " ~ member ~ " (Location l)
+          {
+            return Symbol(TokenKind." ~ member ~ ", l);
+          }");]], [[
+          mixin("static auto " ~ member ~ "()
+          {
+            return Symbol(TokenKind." ~ member ~ ");
+          }");]])[
+        }
+        else
+        {]b4_locations_if([[
+          mixin("static auto " ~ member ~ "(typeof(YYSemanticType." ~
+            visibleTokenTypes[mixin("TokenKind." ~ member)] ~ ") v, Location l)
+          {
+            return Symbol(TokenKind." ~ member ~ ", v, l);
+          }");]], [[
+          mixin("static auto " ~ member ~ "(typeof(YYSemanticType." ~
+             visibleTokenTypes[mixin("TokenKind." ~ member)] ~ ") v)
+          {
+            return Symbol(TokenKind." ~ member ~ ", v);
+          }");]])[
+        }
+      }
+    }]])
+
+
 ## -------------- ##
 ## Symbol kinds.  ##
 ## -------------- ##
@@ -479,5 +533,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 1d2e818d..2904bae5 100644
--- a/tests/calc.at
+++ b/tests/calc.at
@@ -519,6 +519,18 @@ m4_copy([_AT_DATA_CALC_Y(c)], [_AT_DATA_CALC_Y(c++)])
 ## Calc in D.  ##
 ## ----------- ##
 
+# YYLEX_RETURN_WITH_VALUE
+m4_define([YYLEX_RETURN_WITH_VALUE],
+[AT_TOKEN_CTOR_IF([[return Symbol.]AT_TOKEN_PREFIX[$1]($2 AT_LOCATION_IF([[, location]])[);]],
+                  [[return Symbol(TokenKind.]AT_TOKEN_PREFIX[$1, $2]AT_LOCATION_IF([[, location]])[);]])]
+)
+
+# YYLEX_RETURN_WITHOUT_VALUE
+m4_define([YYLEX_RETURN_WITHOUT_VALUE],
+[AT_TOKEN_CTOR_IF([[return Symbol.]AT_TOKEN_PREFIX[$1](AT_LOCATION_IF([[location]])[);]],
+                  [[return Symbol(TokenKind.]AT_TOKEN_PREFIX[$1]AT_LOCATION_IF([[, location]])[);]])]
+)
+
 # AT_CALC_MAIN(d).
 m4_define([AT_CALC_MAIN(d)],
 [[int main (string[] args)
@@ -598,13 +610,13 @@ class CalcLexer(R) : Lexer
 
     // EOF.
     if (input.empty)
-      return Symbol(TokenKind.]AT_TOKEN_PREFIX[EOF]AT_LOCATION_IF([[, location]])[);
+      ]YYLEX_RETURN_WITHOUT_VALUE([EOF])[
 
     // Numbers.
     if (input.front.isNumber)
       {
         value_.ival = parseInt;
-        return Symbol(TokenKind.]AT_TOKEN_PREFIX[NUM, value_.ival]AT_LOCATION_IF([[, location]])[);
+        ]YYLEX_RETURN_WITH_VALUE([NUM], [value_.ival])[
       }
 
     // Individual characters
@@ -622,22 +634,22 @@ class CalcLexer(R) : Lexer
     if (c == '#')
       {
         stderr.writeln (]AT_LOCATION_IF([location, ": ", ])["syntax error: invalid character: '#'");
-        return Symbol(TokenKind.]AT_TOKEN_PREFIX[YYerror]AT_LOCATION_IF([[, location]])[);
+        ]YYLEX_RETURN_WITHOUT_VALUE([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 '+':  ]YYLEX_RETURN_WITHOUT_VALUE([PLUS])[
+      case '-':  ]YYLEX_RETURN_WITHOUT_VALUE([MINUS])[
+      case '*':  ]YYLEX_RETURN_WITHOUT_VALUE([STAR])[
+      case '/':  ]YYLEX_RETURN_WITHOUT_VALUE([SLASH])[
+      case '(':  ]YYLEX_RETURN_WITHOUT_VALUE([LPAR])[
+      case ')':  ]YYLEX_RETURN_WITHOUT_VALUE([RPAR])[
+      case '\n': ]YYLEX_RETURN_WITHOUT_VALUE([EOL])[
+      case '=':  ]YYLEX_RETURN_WITHOUT_VALUE([EQUAL])[
+      case '^':  ]YYLEX_RETURN_WITHOUT_VALUE([POW])[
+      case '!':  ]YYLEX_RETURN_WITHOUT_VALUE([NOT])[
+      default:   ]YYLEX_RETURN_WITHOUT_VALUE([YYUNDEF])[
     }
   }
 }
@@ -1505,6 +1517,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 api.token.constructor %define parse.error custom])
+AT_CHECK_CALC_LALR1_D([%define api.token.constructor %locations %define parse.error detailed])
 
 # ----------------------- #
 # LALR1 Java Calculator.  #
-- 
2.17.1

