bison-patches
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

tests: style: reorder the calculator test macros


From: Akim Demaille
Subject: tests: style: reorder the calculator test macros
Date: Mon, 28 Sep 2020 19:25:36 +0200

Let's make calc.at easier to read.

commit c19c1e7ec5c579c66190ec35b010f6d3dca73f98
Author: Akim Demaille <akim.demaille@gmail.com>
Date:   Sun Sep 27 12:16:31 2020 +0200

    tests: style: reorder the calculator test macros
    
    * tests/local.at (AT_TOKEN_TRANSLATE_IF): New, moved from...
    * tests/calc.at: here.
    Instead of sorting per feature (main, yylex, calc.y) and then by
    language, do the converse, so that C bits are together, etc.

diff --git a/tests/calc.at b/tests/calc.at
index e017e5e4..0da2bbeb 100644
--- a/tests/calc.at
+++ b/tests/calc.at
@@ -19,16 +19,42 @@
 ## Compile the grammar described in the documentation.  ##
 ## ---------------------------------------------------- ##
 
+
+m4_pushdef([AT_CALC_MAIN],  [AT_LANG_DISPATCH([$0], $@)])
+m4_pushdef([AT_CALC_YYLEX], [AT_LANG_DISPATCH([$0], $@)])
+
 # -------------- #
-# AT_CALC_MAIN.  #
+# AT_DATA_CALC.  #
 # -------------- #
 
-m4_pushdef([AT_CALC_MAIN],   [AT_LANG_DISPATCH([$0], $@)])
 
-# Whether token translation is supported.
-m4_pushdef([AT_TOKEN_TRANSLATE_IF],
-[AT_ERROR_CUSTOM_IF([$1], [AT_ERROR_DETAILED_IF([$1], [$2])])])
+# _AT_DATA_CALC_Y($1, $2, $3, [BISON-DIRECTIVES])
+# -----------------------------------------------
+# Produce 'calc.y' and, if %header was specified, 'calc-lex.c' or
+# 'calc-lex.cc'.
+#
+# Don't call this macro directly, because it contains some occurrences
+# of '$1' etc. which will be interpreted by m4.  So you should call it
+# with $1, $2, and $3 as arguments, which is what AT_DATA_CALC_Y does.
+#
+# When %header is not passed, generate a single self-contained file.
+# Otherwise, generate three: calc.y with the parser, calc-lex.c with
+# the scanner, and calc-main.c with "main()".  This is in order to
+# stress the use of the generated parser header.  To avoid code
+# duplication, AT_CALC_YYLEX and AT_CALC_MAIN contain the body of these
+# two later files.
+m4_pushdef([_AT_DATA_CALC_Y],
+[m4_if([$1$2$3], $[1]$[2]$[3], [],
+       [m4_fatal([$0: Invalid arguments: $@])])dnl
+AT_LANG_DISPATCH([$0], $@)])
+
+
 
+## ----------- ##
+## Calc in C.  ##
+## ----------- ##
+
+# AT_CALC_MAIN(c).
 m4_define([AT_CALC_MAIN(c)],
 [[#include <assert.h>
 #include <unistd.h>
@@ -95,44 +121,8 @@ main (int argc, const char **argv)
 }
 ]])
 
-m4_copy([AT_CALC_MAIN(c)], [AT_CALC_MAIN(c++)])
-
-m4_define([AT_CALC_MAIN(d)],
-[[int main (string[] args)
-{]AT_PARAM_IF([[
-  semantic_value result = 0;
-  int count = 0;]])[
-
-  File input = args.length == 2 ? File (args[1], "r") : stdin;
-  auto l = calcLexer (input);
-  auto p = new YYParser (l);]AT_DEBUG_IF([[
-  p.setDebugLevel (1);]])[
-  return !p.parse ();
-}
-]])
-
-
-m4_define([AT_CALC_MAIN(java)],
-[[public static void main (String[] args) throws IOException
-  {]AT_LEXPARAM_IF([[
-    Calc p = new Calc (System.in);]], [[
-    CalcLexer l = new CalcLexer (System.in);
-    Calc p = new Calc (l);]])AT_DEBUG_IF([[
-    p.setDebugLevel (1);]])[
-    boolean success = p.parse ();
-    if (!success)
-      System.exit (1);
-  }
-]])
-
-
-# --------------- #
-# AT_CALC_YYLEX.  #
-# --------------- #
-
-m4_pushdef([AT_CALC_YYLEX],   [AT_LANG_DISPATCH([$0], $@)])
-
 
+# AT_CALC_YYLEX(c).
 m4_define([AT_CALC_YYLEX(c)],
 [[#include <ctype.h>
 
@@ -234,224 +224,6 @@ read_integer (]AT_YYLEX_FORMALS[)
 }
 ]])
 
-m4_copy([AT_CALC_YYLEX(c)], [AT_CALC_YYLEX(c++)])
-
-m4_define([AT_CALC_YYLEX(d)],
-[[import std.range.primitives;
-import std.stdio;
-
-auto calcLexer(R)(R range)
-  if (isInputRange!R && is (ElementType!R : dchar))
-{
-  return new CalcLexer!R(range);
-}
-
-auto calcLexer (File f)
-{
-  import std.algorithm : map, joiner;
-  import std.utf : byDchar;
-
-  return f.byChunk(1024)        // avoid making a syscall roundtrip per char
-          .map!(chunk => cast(char[]) chunk) // because byChunk returns ubyte[]
-          .joiner               // combine chunks into a single virtual range 
of char
-          .calcLexer;           // forward to other overload
-}
-
-class CalcLexer(R) : Lexer
-  if (isInputRange!R && is (ElementType!R : dchar))
-{
-  R input;
-
-  this(R r) {
-    input = r;
-  }
-
-  ]AT_YYERROR_DEFINE[
-
-  YYSemanticType semanticVal_;]AT_LOCATION_IF([[
-  YYLocation location = new YYLocation;
-
-  public final @property YYPosition startPos()
-  {
-    return location.begin;
-  }
-
-  public final @property YYPosition endPos()
-  {
-    return location.end;
-  }
-]])[
-  public final @property YYSemanticType semanticVal()
-  {
-    return semanticVal_;
-  }
-
-  int parseInt ()
-  {
-    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;
-      }
-    return res;
-  }
-
-  TokenKind yylex ()
-  {]AT_LOCATION_IF([[
-    location.begin = location.end;]])[
-
-    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;]])[
-      }
-
-    // EOF.
-    if (input.empty)
-      return TokenKind.CALC_EOF;
-
-    // Numbers.
-    if (input.front.isNumber)
-      {
-        semanticVal_.ival = parseInt;
-        return TokenKind.NUM;
-      }
-
-    // Individual characters
-    auto c = input.front;]AT_LOCATION_IF([[
-    if (c == '\n')
-      {
-        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 TokenKind.YYerror;
-      }
-
-    switch (c)
-    {
-      case '+':  return TokenKind.PLUS;
-      case '-':  return TokenKind.MINUS;
-      case '*':  return TokenKind.STAR;
-      case '/':  return TokenKind.SLASH;
-      case '(':  return TokenKind.LPAR;
-      case ')':  return TokenKind.RPAR;
-      case '\n': return TokenKind.EOL;
-      case '=':  return TokenKind.EQUAL;
-      case '^':  return TokenKind.POW;
-      case '!':  return TokenKind.NOT;
-      default:   return TokenKind.YYUNDEF;
-    }
-  }
-}
-]])
-
-
-m4_define([AT_CALC_YYLEX(java)],
-[AT_LEXPARAM_IF([[%code lexer {]],
-                [[%code epilogue { class CalcLexer implements Calc.Lexer {]])[
-  StreamTokenizer st;]AT_LOCATION_IF([[
-  PositionReader reader;]])[
-
-  public ]AT_LEXPARAM_IF([[YYLexer]], [[CalcLexer]])[ (InputStream is)
-  {]AT_LOCATION_IF([[
-    reader = new PositionReader (new InputStreamReader (is));
-    st = new StreamTokenizer (reader);]], [[
-    st = new StreamTokenizer (new InputStreamReader (is));]])[
-    st.resetSyntax ();
-    st.eolIsSignificant (true);
-    st.wordChars ('0', '9');
-  }
-
-]AT_LOCATION_IF([[
-  Position start = new Position (1, 0);
-  Position end = new Position (1, 0);
-
-  public Position getStartPos () {
-    return new Position (start);
-  }
-
-  public Position getEndPos () {
-    return new Position (end);
-  }
-
-]])[
-  ]AT_YYERROR_DEFINE[
-
-  Integer yylval;
-
-  public Object getLVal () {
-    return yylval;
-  }
-
-  public int yylex () throws IOException {;]AT_LOCATION_IF([[
-    start.set (reader.getPosition ());]])[
-    int tkind = st.nextToken ();]AT_LOCATION_IF([[
-    end.set (reader.getPosition ());]])[
-    switch (tkind)
-      {
-      case StreamTokenizer.TT_EOF:
-        return CALC_EOF;
-      case StreamTokenizer.TT_EOL:;]AT_LOCATION_IF([[
-        end.line += 1;
-        end.column = 0;]])[
-        return (int) '\n';
-      case StreamTokenizer.TT_WORD:
-        yylval = new Integer (st.sval);]AT_LOCATION_IF([[
-        end.set (reader.getPreviousPosition ());]])[
-        return NUM;
-      case ' ': case '\t':
-        return yylex ();
-      case '#':
-        System.err.println(]AT_LOCATION_IF([[start + ": " + ]])["syntax error: 
invalid character: '#'");
-        return YYerror;
-      default:
-        return tkind;
-      }
-  }
-]AT_LEXPARAM_IF([], [[}]])[
-};
-]])
-
-
-# -------------- #
-# AT_DATA_CALC.  #
-# -------------- #
-
-
-# _AT_DATA_CALC_Y($1, $2, $3, [BISON-DIRECTIVES])
-# -----------------------------------------------
-# Produce 'calc.y' and, if %header was specified, 'calc-lex.c' or
-# 'calc-lex.cc'.
-#
-# Don't call this macro directly, because it contains some occurrences
-# of '$1' etc. which will be interpreted by m4.  So you should call it
-# with $1, $2, and $3 as arguments, which is what AT_DATA_CALC_Y does.
-#
-# When %header is not passed, generate a single self-contained file.
-# Otherwise, generate three: calc.y with the parser, calc-lex.c with
-# the scanner, and calc-main.c with "main()".  This is in order to
-# stress the use of the generated parser header.  To avoid code
-# duplication, AT_CALC_YYLEX and AT_CALC_MAIN contain the body of these
-# two later files.
-m4_pushdef([_AT_DATA_CALC_Y],
-[m4_if([$1$2$3], $[1]$[2]$[3], [],
-       [m4_fatal([$0: Invalid arguments: $@])])dnl
-AT_LANG_DISPATCH([$0], $@)])
 
 m4_define([_AT_DATA_CALC_Y(c)],
 [AT_DATA_GRAMMAR([calc.y],
@@ -504,7 +276,6 @@ void location_print (FILE *o, Span s);
 %printer { ]AT_CXX_IF([[yyo << $$]],
                       [[fprintf (yyo, "%d", $$)]])[; } <ival>;
 
-]AT_LANG_MATCH([c\|c++], [[
 %code provides
 {
   #include <stdio.h>
@@ -542,7 +313,6 @@ void location_print (FILE *o, Span s);
     }
   ]])[
 }
-]])[
 
 ]AT_LOCATION_TYPE_SPAN_IF([[
 %initial-action
@@ -571,7 +341,7 @@ input:
 
 line:
   '\n'
-| exp '\n'           { ]AT_PARAM_IF([*result = global_result = $1;], 
[AT_D_IF([], [USE ($1);])])[ }
+| exp '\n'           { ]AT_PARAM_IF([*result = global_result = $1;], [USE 
($1);])[ }
 ;
 
 exp:
@@ -686,8 +456,159 @@ AT_DATA_SOURCE([[calc-main.]AT_LANG_EXT],
 ])# _AT_DATA_CALC_Y(c)
 
 
+
+## ------------- ##
+## Calc in C++.  ##
+## ------------- ##
+
+m4_copy([AT_CALC_MAIN(c)], [AT_CALC_MAIN(c++)])
+m4_copy([AT_CALC_YYLEX(c)], [AT_CALC_YYLEX(c++)])
 m4_copy([_AT_DATA_CALC_Y(c)], [_AT_DATA_CALC_Y(c++)])
 
+
+## ----------- ##
+## Calc in D.  ##
+## ----------- ##
+
+# AT_CALC_MAIN(d).
+m4_define([AT_CALC_MAIN(d)],
+[[int main (string[] args)
+{]AT_PARAM_IF([[
+  semantic_value result = 0;
+  int count = 0;]])[
+
+  File input = args.length == 2 ? File (args[1], "r") : stdin;
+  auto l = calcLexer (input);
+  auto p = new YYParser (l);]AT_DEBUG_IF([[
+  p.setDebugLevel (1);]])[
+  return !p.parse ();
+}
+]])
+
+m4_define([AT_CALC_YYLEX(d)],
+[[import std.range.primitives;
+import std.stdio;
+
+auto calcLexer(R)(R range)
+  if (isInputRange!R && is (ElementType!R : dchar))
+{
+  return new CalcLexer!R(range);
+}
+
+auto calcLexer (File f)
+{
+  import std.algorithm : map, joiner;
+  import std.utf : byDchar;
+
+  return f.byChunk(1024)        // avoid making a syscall roundtrip per char
+          .map!(chunk => cast(char[]) chunk) // because byChunk returns ubyte[]
+          .joiner               // combine chunks into a single virtual range 
of char
+          .calcLexer;           // forward to other overload
+}
+
+class CalcLexer(R) : Lexer
+  if (isInputRange!R && is (ElementType!R : dchar))
+{
+  R input;
+
+  this(R r) {
+    input = r;
+  }
+
+  ]AT_YYERROR_DEFINE[
+
+  YYSemanticType semanticVal_;]AT_LOCATION_IF([[
+  YYLocation location = new YYLocation;
+
+  public final @property YYPosition startPos()
+  {
+    return location.begin;
+  }
+
+  public final @property YYPosition endPos()
+  {
+    return location.end;
+  }
+]])[
+  public final @property YYSemanticType semanticVal()
+  {
+    return semanticVal_;
+  }
+
+  int parseInt ()
+  {
+    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;
+      }
+    return res;
+  }
+
+  TokenKind yylex ()
+  {]AT_LOCATION_IF([[
+    location.begin = location.end;]])[
+
+    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;]])[
+      }
+
+    // EOF.
+    if (input.empty)
+      return TokenKind.CALC_EOF;
+
+    // Numbers.
+    if (input.front.isNumber)
+      {
+        semanticVal_.ival = parseInt;
+        return TokenKind.NUM;
+      }
+
+    // Individual characters
+    auto c = input.front;]AT_LOCATION_IF([[
+    if (c == '\n')
+      {
+        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 TokenKind.YYerror;
+      }
+
+    switch (c)
+    {
+      case '+':  return TokenKind.PLUS;
+      case '-':  return TokenKind.MINUS;
+      case '*':  return TokenKind.STAR;
+      case '/':  return TokenKind.SLASH;
+      case '(':  return TokenKind.LPAR;
+      case ')':  return TokenKind.RPAR;
+      case '\n': return TokenKind.EOL;
+      case '=':  return TokenKind.EQUAL;
+      case '^':  return TokenKind.POW;
+      case '!':  return TokenKind.NOT;
+      default:   return TokenKind.YYUNDEF;
+    }
+  }
+}
+]])
+
 m4_define([_AT_DATA_CALC_Y(d)],
 [AT_DATA_GRAMMAR([calc.y],
 [[/* Infix notation calculator--calc */
@@ -733,7 +654,7 @@ input:
 
 line:
   EOL
-| exp EOL            { ]AT_PARAM_IF([*result = global_result = $1;], 
[AT_D_IF([], [USE ($1);])])[ }
+| exp EOL            { ]AT_PARAM_IF([*result = global_result = $1;], [USE 
($1);])[ }
 ;
 
 exp:
@@ -757,7 +678,7 @@ exp:
 | "-" exp  %prec NEG { $$ = -$2; }
 | exp "^" exp        { $$ = power ($1, $3); }
 | "(" exp ")"        { $$ = $2; }
-| "(" error ")"      { $$ = 1111; ]AT_D_IF([], [yyerrok;])[ }
+| "(" error ")"      { $$ = 1111; yyerrok; }
 | "!"                { $$ = 0; return YYERROR; }
 | "-" error          { $$ = 0; return YYERROR; }
 ;
@@ -778,6 +699,91 @@ power (int base, int exponent)
 AT_CALC_MAIN])
 ])# _AT_DATA_CALC_Y(d)
 
+
+
+## -------------- ##
+## Calc in Java.  ##
+## -------------- ##
+
+m4_define([AT_CALC_MAIN(java)],
+[[public static void main (String[] args) throws IOException
+  {]AT_LEXPARAM_IF([[
+    Calc p = new Calc (System.in);]], [[
+    CalcLexer l = new CalcLexer (System.in);
+    Calc p = new Calc (l);]])AT_DEBUG_IF([[
+    p.setDebugLevel (1);]])[
+    boolean success = p.parse ();
+    if (!success)
+      System.exit (1);
+  }
+]])
+
+m4_define([AT_CALC_YYLEX(java)],
+[AT_LEXPARAM_IF([[%code lexer {]],
+                [[%code epilogue { class CalcLexer implements Calc.Lexer {]])[
+  StreamTokenizer st;]AT_LOCATION_IF([[
+  PositionReader reader;]])[
+
+  public ]AT_LEXPARAM_IF([[YYLexer]], [[CalcLexer]])[ (InputStream is)
+  {]AT_LOCATION_IF([[
+    reader = new PositionReader (new InputStreamReader (is));
+    st = new StreamTokenizer (reader);]], [[
+    st = new StreamTokenizer (new InputStreamReader (is));]])[
+    st.resetSyntax ();
+    st.eolIsSignificant (true);
+    st.wordChars ('0', '9');
+  }
+
+]AT_LOCATION_IF([[
+  Position start = new Position (1, 0);
+  Position end = new Position (1, 0);
+
+  public Position getStartPos () {
+    return new Position (start);
+  }
+
+  public Position getEndPos () {
+    return new Position (end);
+  }
+
+]])[
+  ]AT_YYERROR_DEFINE[
+
+  Integer yylval;
+
+  public Object getLVal () {
+    return yylval;
+  }
+
+  public int yylex () throws IOException {;]AT_LOCATION_IF([[
+    start.set (reader.getPosition ());]])[
+    int tkind = st.nextToken ();]AT_LOCATION_IF([[
+    end.set (reader.getPosition ());]])[
+    switch (tkind)
+      {
+      case StreamTokenizer.TT_EOF:
+        return CALC_EOF;
+      case StreamTokenizer.TT_EOL:;]AT_LOCATION_IF([[
+        end.line += 1;
+        end.column = 0;]])[
+        return (int) '\n';
+      case StreamTokenizer.TT_WORD:
+        yylval = new Integer (st.sval);]AT_LOCATION_IF([[
+        end.set (reader.getPreviousPosition ());]])[
+        return NUM;
+      case ' ': case '\t':
+        return yylex ();
+      case '#':
+        System.err.println(]AT_LOCATION_IF([[start + ": " + ]])["syntax error: 
invalid character: '#'");
+        return YYerror;
+      default:
+        return tkind;
+      }
+  }
+]AT_LEXPARAM_IF([], [[}]])[
+};
+]])
+
 m4_define([_AT_DATA_CALC_Y(java)],
 [AT_DATA_GRAMMAR([Calc.y],
 [[/* Infix notation calculator--calc */
@@ -868,6 +874,12 @@ exp:
 
 
 
+
+## ------------------ ##
+## Calculator tests.  ##
+## ------------------ ##
+
+
 # AT_DATA_CALC_Y([BISON-OPTIONS])
 # -------------------------------
 # Produce 'calc.y' and, if %header was specified, 'calc-lex.c' or
@@ -877,7 +889,6 @@ m4_define([AT_DATA_CALC_Y],
 ])
 
 
-
 # _AT_CHECK_CALC(BISON-OPTIONS, INPUT, [STDOUT], [NUM-STDERR-LINES])
 # ------------------------------------------------------------------
 # Run 'calc' on INPUT and expect no STDOUT nor STDERR.
@@ -1404,6 +1415,5 @@ AT_CHECK_CALC_LALR1_JAVA([%define parse.trace %define 
parse.error custom %locati
 AT_CHECK_CALC_LALR1_JAVA([%define parse.trace %define parse.error verbose 
%locations %lex-param {InputStream is} %define api.push-pull both])
 
 
-m4_popdef([AT_TOKEN_TRANSLATE_IF])
 m4_popdef([AT_CALC_MAIN])
 m4_popdef([AT_CALC_YYLEX])
diff --git a/tests/local.at b/tests/local.at
index 362b28cc..e07f0e51 100644
--- a/tests/local.at
+++ b/tests/local.at
@@ -340,6 +340,10 @@ m4_pushdef([AT_PURE_LEX_IF],
 [AT_PURE_IF([$1],
             [AT_CXX_IF([$1], [$2])])])
 
+# Whether token translation is supported.
+m4_pushdef([AT_TOKEN_TRANSLATE_IF],
+[AT_ERROR_CUSTOM_IF([$1], [AT_ERROR_DETAILED_IF([$1], [$2])])])
+
 m4_pushdef([AT_YYSTYPE],
 [AT_CXX_IF([AT_NAMESPACE[::parser::semantic_type]],
                [AT_API_PREFIX[STYPE]])])
@@ -409,6 +413,7 @@ m4_popdef([AT_YYLTYPE])
 m4_popdef([AT_YYSTYPE])
 m4_popdef([AT_VAL])
 m4_popdef([AT_LOC])
+m4_popdef([AT_TOKEN_TRANSLATE_IF])
 m4_popdef([AT_PURE_LEX_IF])
 m4_popdef([AT_YYERROR_SEES_LOC_IF])
 m4_popdef([AT_YYERROR_ARG_LOC_IF])




reply via email to

[Prev in Thread] Current Thread [Next in Thread]