[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])
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- tests: style: reorder the calculator test macros,
Akim Demaille <=