bison-patches
[Top][All Lists]
Advanced

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

dlang: initial changes to run the calc tests on it


From: Akim Demaille
Subject: dlang: initial changes to run the calc tests on it
Date: Tue, 26 Feb 2019 18:33:55 +0100

Hi HS!

This should give us a track into the test suite.  calc.at is running quite a 
few variations of the calculator, making sure the various features work 
properly.  It should help us introduce proper support for location, %params, 
etc.

What I did below is quite ugly.  In particular, I don't know how to write a 
decent scanner in D.  What I did is truly scary, a way to force C code (with 
gets and ungetc) into my zero knowledge of D.  What is the right way to do the 
following?

File input;

class CalcLexer : Lexer {

  int prev_char = -1; // Uglyyyyyyyy

  int
  get_char ()
  {
    if (prev_char != -1)
      {
        auto res = prev_char;
        prev_char = -1;
        return res;
      }
    else
      {
        auto res = input.rawRead (new char[1]); // Uggggglyyyy tooooo
        if (res.length == 0)
          return -1;
        else
          return res[0];
      }
  }

  void
  unget_char (int c)
  {
    prev_char = c;
  }

  int
  read_signed_integer ()    // HS: scanf to the rescue?
  {
    int c = get_char ();
    int sign = 1;
    int n = 0;

    if (c == '-')
      {
        c = get_char ();
        sign = -1;
      }

    while (isDigit (c))
      {
        n = 10 * n + (c - '0');
        c = get_char ();
      }

    unget_char (c);
    return sign * n;
  }

  YYSemanticType semanticVal_;

  public final @property YYSemanticType semanticVal()
  {
    return semanticVal_;
  }

  int yylex ()
  {
    int c;
    /* Skip white spaces.  */
    do
      {}
    while ((c = get_char ()) == ' ' || c == '\t');

    /* process numbers   */
    if (c == '.' || isDigit (c))
      {
        unget_char (c);
        semanticVal_.ival = read_signed_integer ();
        return YYTokenType.NUM;
      }

    /* Return end-of-file.  */
    if (c == EOF)
    {
      return YYTokenType.EOF;
      }

    /* Return single chars. */
    return c;
  }
}


Cheers!

commit b12f9c76e2206fa5fd83904361eea197c2a714fa
Author: Akim Demaille <address@hidden>
Date:   Mon Feb 18 18:42:41 2019 +0100

    dlang: initial changes to run the calc tests on it
    
    * configure.ac (DCFLAGS): Define.
    * tests/atlocal.in: Receive it.
    * data/skeletons/d.m4 (api.parser.class): Remove spurious YY.
    * data/skeletons/lalr1.d (yylex): Return an int instead of a
    YYTokenType, so that we can use characters as tokens.
    * examples/d/calc.y: Adjust.
    * tests/local.at: Initial support for D.
    (AT_D_IF, AT_DATA_GRAMMAR(D), AT_YYERROR_DECLARE(d))
    (AT_YYERROR_DECLARE_EXTERN(d), AT_YYERROR_DEFINE(d))
    (AT_MAIN_DEFINE(d), AT_COMPILE_D, AT_LANG_COMPILE(d), AT_LANG_EXT(d)):
    New.
    * tests/calc.at: Initial support for D.
    * tests/headers.at

diff --git a/configure.ac b/configure.ac
index 1b5343e7..507e6f64 100644
--- a/configure.ac
+++ b/configure.ac
@@ -192,6 +192,7 @@ BISON_CXX_COMPILER_POSIXLY_CORRECT
 
 # D.
 AC_CHECK_PROGS([DC], [dmd])
+AC_CHECK_PROGS([DCFLAGS], [])
 AM_CONDITIONAL([ENABLE_D], [test x"$DC" != x])
 
 # Java.
diff --git a/data/skeletons/d.m4 b/data/skeletons/d.m4
index 722d04d2..9ebce565 100644
--- a/data/skeletons/d.m4
+++ b/data/skeletons/d.m4
@@ -190,7 +190,7 @@ b4_percent_define_default([[stype]], [[YYSemanticType]])])
 # %name-prefix
 m4_define_default([b4_prefix], [[YY]])
 
-b4_percent_define_default([[api.parser.class]], [b4_prefix[]YYParser])])
+b4_percent_define_default([[api.parser.class]], [b4_prefix[]Parser])])
 m4_define([b4_parser_class], [b4_percent_define_get([[api.parser.class]])])
 
 #b4_percent_define_default([[location_type]], [Location])])
diff --git a/data/skeletons/lalr1.d b/data/skeletons/lalr1.d
index bb5c83fc..07246a77 100644
--- a/data/skeletons/lalr1.d
+++ b/data/skeletons/lalr1.d
@@ -69,7 +69,7 @@ public interface Lexer
    * to the next token and prepares to return the semantic value
    * ]b4_locations_if([and beginning/ending positions ])[of the token.
    * @@return the token identifier corresponding to the next token. */
-  YYTokenType yylex ();
+  int yylex ();
 
   /**
    * Entry point for error reporting.  Emits an error
diff --git a/examples/d/calc.y b/examples/d/calc.y
index 26b4413a..5c7975a3 100644
--- a/examples/d/calc.y
+++ b/examples/d/calc.y
@@ -108,7 +108,7 @@ class CalcLexer : Lexer {
     return semanticVal_;
   }
 
-  YYTokenType yylex ()
+  int yylex ()
   {
     int c;
     /* Skip white spaces.  */
diff --git a/tests/atlocal.in b/tests/atlocal.in
index dc35d95a..f7db41e9 100644
--- a/tests/atlocal.in
+++ b/tests/atlocal.in
@@ -118,6 +118,9 @@ fi
 ## Other.  ##
 ## ------- ##
 
+: ${DC='@DC@'}
+: ${DCFLAGS='@DCFLAGS@'}
+
 # Empty if no javac was found
 : ${CONF_JAVAC='@CONF_JAVAC@'}
 
diff --git a/tests/calc.at b/tests/calc.at
index 47af4d34..252f8ae0 100644
--- a/tests/calc.at
+++ b/tests/calc.at
@@ -44,11 +44,28 @@ m4_define([_AT_DATA_CALC_Y],
 [m4_if([$1$2$3], $[1]$[2]$[3], [],
        [m4_fatal([$0: Invalid arguments: address@hidden)])dnl
 
-m4_pushdef([AT_CALC_MAIN],
-[#include <assert.h>
+AT_D_IF([m4_pushdef([AT_CALC_MAIN],
+[[int main (string[] args)
+{
+  semantic_value result = 0;
+  int count = 0;
+
+  if (args.length == 2)
+    input = File (args[1], "r");
+  else
+    input = stdin;
+
+  CalcLexer l = new CalcLexer ();
+  YYParser p = new YYParser (l);
+  int status = !p.parse ();
+  return status;
+}
+]])],
+[m4_pushdef([AT_CALC_MAIN],
+[[#include <assert.h>
 #include <unistd.h>
 
-AT_CXX_IF([[
+]AT_CXX_IF([[
 namespace
 {
   /* A C++ ]AT_NAME_PREFIX[parse that simulates the C signature.  */
@@ -101,10 +118,103 @@ main (int argc, const char **argv)
   assert (global_count == count);   (void) count;
   return status;
 }
-]])
+]])])
+
+AT_D_IF([m4_pushdef([AT_CALC_LEX],
+[[File input;
+
+class CalcLexer : Lexer {
+
+  int prev_char = -1;
+
+  int
+  get_char ()
+  {
+    if (prev_char != -1)
+      {
+        auto res = prev_char;
+        prev_char = -1;
+        return res;
+      }
+    else
+      {
+        auto res = input.rawRead (new char[1]);
+        if (res.length == 0)
+          return -1;
+        else
+          return res[0];
+      }
+  }
+
+  void
+  unget_char (int c)
+  {
+    prev_char = c;
+  }
+
+  public void yyerror (string s)
+  {
+    stderr.writeln (s);
+  }
+
+  int
+  read_signed_integer ()
+  {
+    int c = get_char ();
+    int sign = 1;
+    int n = 0;
+
+    if (c == '-')
+      {
+        c = get_char ();
+        sign = -1;
+      }
+
+    while (isDigit (c))
+      {
+        n = 10 * n + (c - '0');
+        c = get_char ();
+      }
+
+    unget_char (c);
+    return sign * n;
+  }
 
+  YYSemanticType semanticVal_;
 
-m4_pushdef([AT_CALC_LEX],
+  public final @property YYSemanticType semanticVal()
+  {
+    return semanticVal_;
+  }
+
+  int yylex ()
+  {
+    int c;
+    /* Skip white spaces.  */
+    do
+      {}
+    while ((c = get_char ()) == ' ' || c == '\t');
+
+    /* process numbers   */
+    if (c == '.' || isDigit (c))
+      {
+        unget_char (c);
+        semanticVal_.ival = read_signed_integer ();
+        return YYTokenType.NUM;
+      }
+
+    /* Return end-of-file.  */
+    if (c == EOF)
+    {
+      return YYTokenType.EOF;
+      }
+
+    /* Return single chars. */
+    return c;
+  }
+}
+]])],
+[m4_pushdef([AT_CALC_LEX],
 [[#include <ctype.h>
 
 ]AT_YYLEX_DECLARE_EXTERN[
@@ -202,12 +312,19 @@ read_signed_integer (]AT_YYLEX_FORMALS[)
   return c;
 }
 ]])
+])
 
 AT_DATA_GRAMMAR([calc.y],
 [[/* Infix notation calculator--calc */
-]$4
-AT_CXX_IF(
-[%define global_tokens_and_yystype])[
+]$4[
+]AT_CXX_IF([%define global_tokens_and_yystype])[
+]AT_D_IF([[
+%code imports {
+  import std.ascii;
+  import std.stdio;
+  alias semantic_value = int;
+}
+]], [[
 %code requires
 {
 ]AT_LOCATION_TYPE_SPAN_IF([[
@@ -241,6 +358,7 @@ AT_CXX_IF(
   /* Exercise pre-prologue dependency to %union.  */
   typedef int semantic_value;
 }
+]])[
 
 /* Exercise %union. */
 %union
@@ -250,6 +368,7 @@ AT_CXX_IF(
 %printer { ]AT_CXX_IF([[yyo << $$]],
                       [[fprintf (yyo, "%d", $$)]])[; } <ival>;
 
+]AT_D_IF([], [[
 %code provides
 {
   #include <stdio.h>
@@ -271,6 +390,7 @@ static int power (int base, int exponent);
 ]AT_YYERROR_DECLARE[
 ]AT_YYLEX_DECLARE_EXTERN[
 }
+]])[
 
 ]AT_LOCATION_TYPE_SPAN_IF([[
 %initial-action
@@ -299,15 +419,17 @@ input:
 
 line:
   '\n'
-| exp '\n'           { ]AT_PARAM_IF([*result = global_result = $1], [USE 
($1)])[; }
+| exp '\n'           { ]AT_PARAM_IF([*result = global_result = $1;], 
[AT_D_IF([], [USE ($1);])])[ }
 ;
 
 exp:
   NUM
 | exp '=' exp
   {
-    if ($1 != $3)
-      fprintf (stderr, "calc: error: %d != %d\n", $1, $3);
+    if ($1 != $3)]AT_D_IF([
+      stderr.writefln ("calc: error: %d != %d", $1, $3);], [
+      fprintf (stderr, "calc: error: %d != %d\n", $1, $3);], [
+      ])[
     $$ = $1;
   }
 | exp '+' exp        { $$ = $1 + $3;        }
@@ -317,9 +439,9 @@ exp:
 | '-' exp  %prec NEG { $$ = -$2;            }
 | exp '^' exp        { $$ = power ($1, $3); }
 | '(' exp ')'        { $$ = $2;             }
-| '(' error ')'      { $$ = 1111; yyerrok;  }
-| '!'                { $$ = 0; YYERROR;     }
-| '-' error          { $$ = 0; YYERROR;     }
+| '(' error ')'      { $$ = 1111; ]AT_D_IF([], [yyerrok;])[  }
+| '!'                { $$ = 0; ]AT_D_IF([return YYERROR], [YYERROR])[;     }
+| '-' error          { $$ = 0; ]AT_D_IF([return YYERROR], [YYERROR])[;     }
 ;
 %%
 
@@ -730,3 +852,23 @@ AT_CHECK_CALC_GLR_CC([%define parse.error verbose %debug 
%name-prefix "calc" %de
 
 AT_CHECK_CALC_GLR_CC([%locations %defines %define parse.error verbose %debug 
%name-prefix "calc" %verbose %yacc %parse-param {semantic_value *result} 
%parse-param {int *count}])
 AT_CHECK_CALC_GLR_CC([%locations %defines %define parse.error verbose %debug 
%define api.prefix {calc} %verbose %yacc %parse-param {semantic_value *result} 
%parse-param {int *count}])
+
+
+
+# --------------------------- #
+# Simple LALR1 D Calculator.  #
+# --------------------------- #
+
+AT_BANNER([[Simple LALR(1) D Calculator.]])
+
+# First let's try using %skeleton
+AT_CHECK_CALC([%skeleton "lalr1.d"])
+
+# AT_CHECK_CALC_LALR1_D([BISON-OPTIONS])
+# ---------------------------------------
+# Start a testing chunk which compiles 'calc' grammar with
+# the C++ skeleton, and performs several tests over the parser.
+m4_define([AT_CHECK_CALC_LALR1_D],
+[AT_CHECK_CALC([%language "D" $1], [$2])])
+
+AT_CHECK_CALC_LALR1_D([])
\ No newline at end of file
diff --git a/tests/local.at b/tests/local.at
index 58e35a9a..8ba82c4f 100644
--- a/tests/local.at
+++ b/tests/local.at
@@ -147,13 +147,16 @@ m4_pushdef([AT_DEBUG_IF],
 [m4_bmatch([$3], [%debug\|%define parse.trace], [$1], [$2])])
 m4_pushdef([AT_CXX_IF],
 [m4_bmatch([$3], [%language "[Cc]\+\+"\|%skeleton "[a-z0-9]+\.cc"], [$1], 
[$2])])
+m4_pushdef([AT_D_IF],
+[m4_bmatch([$3], [%language "[Dd]"\|%skeleton "[a-z0-9]+\.d"], [$1], [$2])])
 m4_pushdef([AT_JAVA_IF],
 [m4_bmatch([$3], [%language "[Jj][Aa][Vv][Aa]"\|%skeleton "[a-z0-9]+\.java"], 
[$1], [$2])])
 # The target language: "c", "c++", or "java".
 m4_pushdef([AT_LANG],
 [AT_JAVA_IF([java],
             [AT_CXX_IF([c++],
-                       [c])])])
+                       [AT_D_IF([d],
+                                [c])])])])
 m4_pushdef([AT_GLR_IF],
 [m4_bmatch([$3], [%glr-parser\|%skeleton "glr\..*"], [$1], [$2])])
 m4_pushdef([AT_LALR1_CC_IF],
@@ -633,6 +636,40 @@ done
 CXXFLAGS=$at_for_each_std_CXXFLAGS_save
 ]])
 
+
+
+## --- ##
+## D.  ##
+## --- ##
+
+# AT_DATA_GRAMMAR(NAME, CONTENT)
+# ------------------------------
+m4_copy([AT_DATA], [AT_DATA_GRAMMAR(d)])
+
+
+# No need to declare, it's part of the class interface.
+m4_define([AT_YYERROR_DECLARE(d)],        [])
+m4_define([AT_YYERROR_DECLARE_EXTERN(d)], [])
+
+m4_define([AT_YYERROR_DEFINE(d)],
+[[/* An error reporting function.  */
+public void error (]AT_LOCATION_IF([[location_type l, ]])[string m)
+{
+  // FIXME: location.
+  stderr.writeln (m);
+}]])
+
+
+m4_define([AT_MAIN_DEFINE(d)],
+[[int main ()
+{
+  Lexer l = new Lexer ();
+  Parser p = new Parser (l);
+  p.parse ();
+}]])
+
+
+
 ## ------ ##
 ## Java.  ##
 ## ------ ##
@@ -921,6 +958,25 @@ AT_CHECK(m4_join([ ],
          0, [ignore], [ignore])])
 
 
+# AT_COMPILE_D(OUTPUT, [SOURCES = OUTPUT.d], [EXTRA-COMPILER-FLAGS])
+# -------------------------------------------------------------------
+# Compile SOURCES into OUTPUT.  If the C++ compiler does not work,
+# ignore the test.
+#
+# If OUTPUT does not contain '.', assume that we are linking too,
+# otherwise pass "-c"; this is a hack.  The default SOURCES is OUTPUT
+# with trailing ".o" removed, and ".cc" appended.
+m4_define([AT_COMPILE_D],
+[AT_KEYWORDS(d)
+AT_CHECK(m4_join([ ],
+                 [$DC $DCFLAGS $3],
+                 [m4_bmatch([$1], [[.]], [-c])],
+                 [-of$1],
+                 [m4_default([$2], [m4_bpatsubst([$1], [\.o$]).d])],
+                 [m4_bmatch([$1], [[.]], [], [$LIBS])]),
+         0, [ignore], [ignore])])
+
+
 # AT_JAVA_COMPILE(SOURCES)
 # ------------------------
 # Compile SOURCES into Java class files.  Skip the test if java or javac
@@ -948,6 +1004,7 @@ m4_define([AT_LANG_FOR_EACH_STD],  [AT_LANG_DISPATCH([$0], 
$@)])
 m4_define([AT_LANG_COMPILE],  [AT_LANG_DISPATCH([$0], $@)])
 m4_define([AT_LANG_COMPILE(c)],    [AT_COMPILE([$1], [$2], [$3])])
 m4_define([AT_LANG_COMPILE(c++)],  [AT_COMPILE_CXX([$1], [$2], [$3])])
+m4_define([AT_LANG_COMPILE(d)],    [AT_COMPILE_D([$1], [$2], [$3])])
 m4_define([AT_LANG_COMPILE(java)], [AT_JAVA_COMPILE([$1.java], [$2], [$3])])
 
 
@@ -957,6 +1014,7 @@ m4_define([AT_LANG_COMPILE(java)], 
[AT_JAVA_COMPILE([$1.java], [$2], [$3])])
 m4_define([AT_LANG_EXT],  [AT_LANG_DISPATCH([$0], $@)])
 m4_define([AT_LANG_EXT(c)],    [c])
 m4_define([AT_LANG_EXT(c++)],  [cc])
+m4_define([AT_LANG_EXT(d)],    [d])
 m4_define([AT_LANG_EXT(java)], [java])
 
 




reply via email to

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