poke-devel
[Top][All Lists]
Advanced

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

Re: [PATCH] pokefmt: add template system to embed Poke code in files


From: Jose E. Marchesi
Subject: Re: [PATCH] pokefmt: add template system to embed Poke code in files
Date: Sat, 23 Sep 2023 12:14:32 +0200
User-agent: Gnus/5.13 (Gnus v5.13)

> 2023-09-22  Mohammad-Reza Nabipoor  <mnabipoor@gnu.org>
>
>       * configure.ac (AC_CONFIG_FILES): Add `pokefmt/Makefile'.
>       * Makefile.am (SUBDIRS): Add `pokefmt'.
>       * pokefmt/pokefmt.l: New template system to embed Poke code in docs,
>       tests, etc.
>       * pokefmt/pokefmt.pk: Poke support code for `pokefmt' app.
>       * pokefmt/Makefile.am: New file.
>       * run.in (POKEFMTAPPDIR): New variable.
>       * poke.pokefmt/pokefmt.exp: New tests.
>       * testsuite/Makefile.am (check-DEJAGNU): Add `POKEFMTAPPDIR' env var.
>       (EXTRA_DIST): Update.
>       * doc/poke.texi (pokefmt): Add new chapter.
> ---
>
> Hello Jose.
>
> This is `pokefmt' with tests and documentation!
>
>
> Regards,
> Mohammad-Reza
>
>
>  ChangeLog                          |  14 +
>  Makefile.am                        |   2 +-
>  configure.ac                       |   1 +
>  doc/poke.texi                      |  88 ++++
>  pokefmt/Makefile.am                |  83 ++++
>  pokefmt/pokefmt.l                  | 623 +++++++++++++++++++++++++++++
>  pokefmt/pokefmt.pk                 |  47 +++
>  run.in                             |   1 +
>  testsuite/Makefile.am              |   2 +
>  testsuite/poke.pokefmt/pokefmt.exp | 115 ++++++
>  10 files changed, 975 insertions(+), 1 deletion(-)
>  create mode 100644 pokefmt/Makefile.am
>  create mode 100644 pokefmt/pokefmt.l
>  create mode 100644 pokefmt/pokefmt.pk
>  create mode 100644 testsuite/poke.pokefmt/pokefmt.exp
>
> diff --git a/ChangeLog b/ChangeLog
> index 00455319..2b834e32 100644
> --- a/ChangeLog
> +++ b/ChangeLog
> @@ -1,3 +1,17 @@
> +2023-09-22  Mohammad-Reza Nabipoor  <mnabipoor@gnu.org>
> +
> +     * configure.ac (AC_CONFIG_FILES): Add `pokefmt/Makefile'.
> +     * Makefile.am (SUBDIRS): Add `pokefmt'.
> +     * pokefmt/pokefmt.l: New template system to embed Poke code in docs,
> +     tests, etc.
> +     * pokefmt/pokefmt.pk: Poke support code for `pokefmt' app.
> +     * pokefmt/Makefile.am: New file.
> +     * run.in (POKEFMTAPPDIR): New variable.
> +     * poke.pokefmt/pokefmt.exp: New tests.
> +     * testsuite/Makefile.am (check-DEJAGNU): Add `POKEFMTAPPDIR' env var.
> +     (EXTRA_DIST): Update.
> +     * doc/poke.texi (pokefmt): Add new chapter.
> +
>  2023-09-18  Jose E. Marchesi  <jemarch@gnu.org>
>  
>       * libpoke/pkl-lex.l: Accept t and T suffix for uint<1> values.
> diff --git a/Makefile.am b/Makefile.am
> index dec75aac..34c50b80 100644
> --- a/Makefile.am
> +++ b/Makefile.am
> @@ -17,7 +17,7 @@
>  
>  ACLOCAL_AMFLAGS = -I m4 -I m4/libpoke
>  SUBDIRS = jitter gl autoconf maps pickles gl-libpoke libpoke poke \
> -          poked utils doc man testsuite etc po
> +          poked pokefmt utils doc man testsuite etc po
>  
>  EXTRA_DIST = INSTALL.generic DEPENDENCIES $(top_srcdir)/.version
>  BUILT_SOURCES = $(top_srcdir)/.version
> diff --git a/configure.ac b/configure.ac
> index 6811f4d8..5bb9835a 100644
> --- a/configure.ac
> +++ b/configure.ac
> @@ -277,6 +277,7 @@ AC_CONFIG_FILES(Makefile
>                  libpoke/pkl-config.pk
>                  poke/Makefile
>                  poked/Makefile
> +                pokefmt/Makefile
>                  utils/Makefile
>                  pickles/Makefile
>                  maps/Makefile
> diff --git a/doc/poke.texi b/doc/poke.texi
> index 136b10d0..3fc5df8b 100644
> --- a/doc/poke.texi
> +++ b/doc/poke.texi
> @@ -77,6 +77,9 @@ Pickles
>  poked
>  * poked::                       The poke daemon.
>  
> +pokefmt
> +* pokefmt::                     The poke template system.
> +
>  poke and Emacs
>  * Programming Emacs Modes::     Modes to write Poke and RAS programs.
>  
> @@ -8581,6 +8584,91 @@ An array of callbacks to be run after executing the 
> @emph{command}
>  (received from input channel 2).  Callback signature: @code{()void}.
>  @end table
>  
> +@node pokefmt
> +@chapter pokefmt
> +
> +@code{pokefmt} is a template system to embed Poke code in files.
> +It copies the content from standard input to standard output and
> +replaces the Poke code with the result of execution.  You can write
> +Poke code between @code{%@{} and @code{@}%} delimters.  To simplify

s/delimters/delimiters/.

> +printing of expressions, @code{pokefmt} also supports embedding of
> +Poke expressions between @code{%(} and @code{%)} delimiters.
> +
> +
> +As as example, @code{pokefmt} will transform the following text
> +
> +@example
> +%@{
> +  load riscv;
> +
> +  var a = 1, a = 2;
> +@}%
> +The encoding of ADD R3, R2, R1 instruction in RISC-V instruction set
> +is %@{printf "0x%u32x", rv32_add (3, 2, 1);@}%.
> +
> +The result of %(a)% + %(b)% is %(a + b)%.
> +@end example
> +
> +to this text:
> +
> +@example
> +
> +The encoding of ADD R3, R2, R1 instruction in RISC-V instruct set
> +is 0x001101b3.
> +
> +The result of 1 + 2 is 3.
> +@end example
> +
> +
> +You can customize the printer for expressions by re-defining
> +@code{pokefmt_expr_printer} function.  For example,
> +
> +
> +@example
> +#as: -march=rv32i
> +#objdump: -dr
> +%@{
> +  load riscv;
> +  fun pokefmt_expr_printer = (any i32) void:
> +  @{
> +    printf ("%u32x", i32 as uint<32>);
> +  @}
> +@}%
> +.*:[    ]+file format .*
> +
> +
> +Disassembly of section .text:
> +
> +0+000 <target>:
> +#...
> +[       ]+40:[  ]+%(+(rv32_auipc :rd 0 :imm 0))%[  ]+auipc[        ]+zero,0x0
> +%@{
> +  /* Change the expression printer to accept RV32_Insn.  */
> +  fun pokefmt_expr_printer = (any rv32insn) void:
> +  @{
> +    printf ("%u32x", +(rv32insn as RV32_Insn));
> +  @}
> +@}%[       ]+44:[  ]+%(rv32_lw :rd 0 :rs1 0 :imm 0)%[  ]+lw[   
> ]+zero,0\(zero\) # 0 .*
> +@end example
> +
> +will be transformed to:
> +
> +@example
> +#as: -march=rv32i
> +#objdump: -dr
> +
> +.*:[    ]+file format .*
> +
> +
> +Disassembly of section .text:
> +
> +0+000 <target>:
> +#...
> +[       ]+40:[  ]+00000017[  ]+auipc[        ]+zero,0x0
> +[       ]+44:[  ]+00002003[  ]+lw[   ]+zero,0\(zero\) # 0 .*
> +@end example
> +
> +
>  @node Programming Emacs Modes
>  @chapter Programming Emacs Modes
>  
> diff --git a/pokefmt/Makefile.am b/pokefmt/Makefile.am
> new file mode 100644
> index 00000000..8f75b166
> --- /dev/null
> +++ b/pokefmt/Makefile.am
> @@ -0,0 +1,83 @@
> +# pokefmt Makefile.am
> +
> +# Copyright (C) 2023 Mohammad-Reza Nabipoor
> +
> +# This program is free software: you can redistribute it and/or modify
> +# it under the terms of the GNU General Public License as published by
> +# the Free Software Foundation, either version 3 of the License, or
> +# (at your option) any later version.
> +#
> +# This program is distributed in the hope that it will be useful,
> +# but WITHOUT ANY WARRANTY; without even the implied warranty of
> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> +# GNU General Public License for more details.
> +#
> +# You should have received a copy of the GNU General Public License
> +# along with this program.  If not, see <http://www.gnu.org/licenses/>.
> +
> +AUTOMAKE_OPTIONS = subdir-objects
> +
> +MOSTLYCLEANFILES =
> +CLEANFILES =
> +DISTCLEANFILES =
> +MAINTAINERCLEANFILES =
> +
> +BUILT_SOURCES =
> +
> +EXTRA_DIST =
> +
> +appdir = $(pkgdatadir)/pokefmt
> +
> +dist_app_DATA = pokefmt.pk
> +
> +AM_LFLAGS = -d
> +# The Automake generated .l.c rule is broken: When executed in a VPATH build,
> +#+   - The .c file gets generated in the build directory. But since it 
> requires
> +#     special tools to rebuild it, we need to distribute it in the tarballs,
> +#     and by the GNU Coding Standards
> +#     <https://www.gnu.org/prep/standards/html_node/Makefile-Basics.html>
> +#     the file should be generated in the source directory.
> +#   - The #line directives in the .c file refer to a nonexistent file once it
> +#     has been moved from the build directory to the source directory. This
> +#     leads to error if 'lcov' is used later.
> +# Additionally, here we assume Flex and therefore don't need the ylwrap 
> script.
> +# Therefore we override this rule.
> +# Since this is a rule that produces multiple files, we apply the idiom from
> +# <https://lists.gnu.org/archive/html/bug-make/2020-09/msg00008.html>, so 
> that
> +# it works also in parallel 'make'.
> +generate-pokefmt:
> +     $(AM_V_LEX)$(LEX) $(LFLAGS) $(AM_LFLAGS) -t $(srcdir)/pokefmt.l > 
> pokefmt.c \
> +     && test ':' = '$(LEX)' || { \
> +       sed -e 's|".*/pokefmt\.l"|"pokefmt.l"|' \
> +           -e 's|"lex\.yy\.c"|"pokefmt.c"|' \
> +           < pokefmt.c > pokefmt.c-tmp \
> +       && sed -e 's|".*/pokefmt\.l"|"pokefmt.l"|' \
> +              < pokefmt.h > pokefmt.h-tmp \
> +       && rm -f pokefmt.c pokefmt.h \
> +       && mv pokefmt.c-tmp $(srcdir)/pokefmt.c \
> +       && mv pokefmt.h-tmp $(srcdir)/pokefmt.h; \
> +     }
> +.PHONY: generate-pokefmt
> +# The above rule will generate files with time-stamp order
> +# pokefmt.l <= pokefmt.c <= pokefmt.h.
> +pokefmt.c: pokefmt.l
> +     @{ test -f $(srcdir)/pokefmt.c && test ! $(srcdir)/pokefmt.c -ot 
> $(srcdir)/pokefmt.l; } || $(MAKE) generate-pokefmt
> +pokefmt.h: pokefmt.c
> +     @{ test -f $(srcdir)/pokefmt.h && test ! $(srcdir)/pokefmt.h -ot 
> $(srcdir)/pokefmt.c; } || $(MAKE) generate-pokefmt
> +BUILT_SOURCES += pokefmt.c pokefmt.h
> +MOSTLYCLEANFILES += pokefmt.c-tmp pokefmt.h-tmp
> +MAINTAINERCLEANFILES += $(srcdir)/pokefmt.c $(srcdir)/pokefmt.h
> +EXTRA_DIST += pokefmt.l pokefmt.c pokefmt.h
> +
> +bin_PROGRAMS = pokefmt
> +pokefmt_SOURCES = pokefmt.c pokefmt.h
> +
> +pokefmt_SOURCES += ../common/pk-utils.c ../common/pk-utils.h
> +
> +pokefmt_CPPFLAGS = -I$(top_srcdir)/common \
> +                   -I$(top_builddir)/gl -I$(top_srcdir)/gl \
> +                   -I$(top_srcdir)/libpoke -I$(top_builddir)/libpoke
> +pokefmt_CFLAGS = -Wall
> +pokefmt_LDADD = $(top_builddir)/gl/libgnu.la \
> +                $(top_builddir)/libpoke/libpoke.la

Using the same gnulib module than the poke application is ok I guess,
even if it will probably bring in a lot of stuff that pokefmt doesn't
need.

> +pokefmt_LDFLAGS =
> diff --git a/pokefmt/pokefmt.l b/pokefmt/pokefmt.l
> new file mode 100644
> index 00000000..fffda29f
> --- /dev/null
> +++ b/pokefmt/pokefmt.l
> @@ -0,0 +1,623 @@
> +
> +/* pokefmt.l - Template system to embed Poke code in files.  */
> +
> +/* Copyright (C) 2023 Mohammad-Reza Nabipoor.  */
> +
> +/* This program is free software: you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation, either version 3 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program.  If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +/* clang-format off */
> +
> +%option noyywrap 8bit warn nodefault noinput nounput reentrant
> +%option outfile="pokefmt.c"
> +%option header-file="pokefmt.h"
> +%option extra-type="struct source_range *"
> +
> +%top{
> +#include <config.h>
> +}
> +
> +%x POKE_STMT POKE_EXPR
> +
> +%{
> +#define POKE_STMT_PARTIAL (POKE_EXPR + 1)
> +#define POKE_EXPR_PARTIAL (POKE_EXPR + 2)
> +%}
> +
> +%{
> +/* line[1], col[1] pair represents the current location.
> +   line[0], col[0] pair represents the beginning of current/previous
> +   Poke code section.
> +
> +   The range is a half-open interval [(l0,c0), (l1,c1)).  */
> +struct source_range
> +{
> +  int line[2];
> +  int col[2];
> +};
> +%}
> +
> +%%
> +
> +"\\%{"  {
> +          yyextra->col[1] += 3;
> +          fwrite (yytext + 1, (size_t) yyleng - 1, 1, yyout);
> +        }
> +"\\%("  {
> +          yyextra->col[1] += 3;
> +          fwrite (yytext + 1, (size_t) yyleng - 1, 1, yyout);
> +        }
> +
> +"%{"    {
> +          yyextra->line[0] = yyextra->line[1];
> +          yyextra->col[0] = yyextra->col[1];
> +          yyextra->col[1] += 2;
> +          BEGIN (POKE_STMT);
> +        }
> +"%("    {
> +          yyextra->line[0] = yyextra->line[1];
> +          yyextra->col[0] = yyextra->col[1];
> +          yyextra->col[1] += 2;
> +          BEGIN (POKE_EXPR);
> +        }
> +
> +<POKE_STMT>{
> +
> +[^}\n]*       |
> +"}"+[^}%\n]*  {
> +                yyextra->col[1] += yyleng;
> +                return POKE_STMT_PARTIAL;
> +              }
> +\n+           {
> +                yyextra->line[1] += yyleng;
> +                yyextra->col[1] = 1;
> +                return POKE_STMT_PARTIAL;
> +              }
> +"}"+"%"       {
> +                yyextra->col[1] += yyleng;
> +                BEGIN (INITIAL);
> +                return POKE_STMT;
> +              }
> +
> +}
> +
> +<POKE_EXPR>{
> +
> +[^)\n]*       |
> +")"+[^)%\n]*  {
> +                yyextra->col[1] += yyleng;
> +                return POKE_EXPR_PARTIAL;
> +              }
> +\n+           {
> +                yyextra->line[1] += yyleng;
> +                yyextra->col[1] = 1;
> +                return POKE_EXPR_PARTIAL;
> +              }
> +")"+"%"       {
> +                yyextra->col[1] += yyleng;
> +                BEGIN (INITIAL);
> +                return POKE_EXPR;
> +              }
> +
> +}
> +
> +.   {
> +      ++yyextra->col[1];
> +      ECHO;
> +    }
> +\n  {
> +      ++yyextra->line[1];
> +      yyextra->col[1] = 1;
> +      ECHO;
> +    }
> +
> +%%
> +
> +// clang-format on
> +
> +#include <assert.h>
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <string.h>
> +
> +#include <err.h>
> +#include <getopt.h>
> +#include <unistd.h>
> +
> +#include "configmake.h"
> +#include "libpoke.h"
> +#include "pk-utils.h"
> +
> +struct poke
> +{
> +  pk_compiler compiler;
> +  FILE *output;
> +  char *filename;
> +};
> +
> +void poke_init (struct poke *, const char *poke_src_file, FILE *output);
> +void poke_free (struct poke *);
> +
> +static void poke_stmt (struct poke *, const char *poke_src,
> +                       const struct source_range *);
> +static void poke_expr (struct poke *, const char *poke_src,
> +                       const struct source_range *);
> +
> +static struct pokefmt_opts
> +{
> +  /* Poke codes provided by user on the command line.  */
> +  size_t n_codes;
> +  char **codes;
> +} pokefmt_opts;
> +
> +static void pokefmt_opts_init (int argc, char *argv[]);
> +static void pokefmt_opts_free ();
> +
> +int
> +main (int argc, char *argv[])
> +{
> +  struct poke poke;
> +  yyscan_t scanner;
> +  int section_type;
> +  struct source_range range = {
> +    .line = { 1, 1 },
> +    .col = { 1, 1 },
> +  };
> +  char *poke_src;
> +  size_t poke_src_len;
> +  size_t poke_src_cap;
> +
> +  pokefmt_opts_init (argc, argv);
> +
> +  poke_src_len = 0;
> +  poke_src_cap = 1024;
> +  poke_src = malloc (poke_src_cap);
> +  if (poke_src == NULL)
> +    err (1, "malloc() failed");
> +
> +#define SRC_APPEND(src, srclen)                                              
>  \
> +  do                                                                         
>  \
> +    {                                                                        
>  \
> +      size_t _srclen = (srclen);                                             
>  \
> +      size_t _rem = poke_src_cap - poke_src_len;                             
>  \
> +      if (_rem < _srclen + 1)                                                
>  \
> +        {                                                                    
>  \
> +          poke_src_cap += _srclen + 1024;                                    
>  \
> +          poke_src = realloc (poke_src, poke_src_cap);                       
>  \
> +          if (poke_src == NULL)                                              
>  \
> +            err (1, "realloc() failed");                                     
>  \
> +        }                                                                    
>  \
> +      memcpy (poke_src + poke_src_len, (src), _srclen);                      
>  \
> +      poke_src_len += _srclen;                                               
>  \
> +      poke_src[poke_src_len] = '\0';                                         
>  \
> +    }                                                                        
>  \
> +  while (0)
> +
> +  poke_init (&poke, /*FIXME*/ NULL, stdout);
> +
> +  if (yylex_init_extra (&range, &scanner) != 0)
> +    err (1, "yylex_init_extra() failed");
> +
> +  pokefmt_opts_free ();
> +
> +  while ((section_type = yylex (scanner)))
> +    {
> +      char *chunk;
> +      int chunk_len;
> +
> +      switch (section_type)
> +        {
> +        case POKE_STMT:
> +        case POKE_EXPR:
> +        case POKE_STMT_PARTIAL:
> +        case POKE_EXPR_PARTIAL:
> +          chunk = yyget_text (scanner);
> +          chunk_len = yyget_leng (scanner);
> +          break;
> +        default:
> +          PK_UNREACHABLE ();
> +        }
> +
> +      switch (section_type)
> +        {
> +        case POKE_STMT:
> +        case POKE_EXPR:
> +          chunk_len -= 2; /* Skip "}%" or ")%".  */
> +        }
> +      SRC_APPEND (chunk, chunk_len);
> +
> +      switch (section_type)
> +        {
> +        case POKE_STMT:
> +          poke_stmt (&poke, poke_src, &range);
> +          poke_src_len = 0;
> +          break;
> +        case POKE_EXPR:
> +          SRC_APPEND (";", 1);
> +          poke_expr (&poke, poke_src, &range);
> +          poke_src_len = 0;
> +          break;
> +        }
> +    }
> +
> +  free (poke_src);
> +  yylex_destroy (scanner);
> +  poke_free (&poke);
> +  return 0;
> +}
> +
> +static void
> +pokefmt_version (void)
> +{
> +  printf ("pokefmt (GNU poke) %s\n\n", VERSION);
> +  printf ("\
> +Copyright (C) %s The poke authors.\n\
> +License GPLv3+: GNU GPL version 3 or later",
> +          "2023");
> +  puts (".\n\
> +This is free software: you are free to change and redistribute it.\n\
> +There is NO WARRANTY, to the extent permitted by law.");
> +}
> +
> +// clang-format off
> +
> +static void
> +pokefmt_help (void)
> +{
> +  printf ("Usage: pokefmt [OPTION]... [POKE-CODE]..." "\n"
> +          "Template system to process embedded code in files." "\n"
> +          "" "\n"
> +          "  -h, --help              print a help message and exit" "\n"
> +          "  -v, --version           show version and exit" "\n"
> +          "" "\n"
> +          "Report bugs in the bug tracker at" "\n"
> +          "  %s" "\n"
> +          "  or by email to <%s>." "\n",
> +          PACKAGE_BUGZILLA, PACKAGE_BUGREPORT);
> +
> +#ifdef PACKAGE_PACKAGER_BUG_REPORTS
> +  printf ("Report %s bugs to: %s\n", PACKAGE_PACKAGER,
> +          PACKAGE_PACKAGER_BUG_REPORTS);
> +#endif
> +  printf ("%s home page: <%s>" "\n"
> +"General help using GNU software: <http://www.gnu.org/gethelp/>\n",
> +        PACKAGE_NAME, PACKAGE_URL);
> +}
> +
> +// clang-format on
> +
> +static void
> +pokefmt_opts_init (int argc, char *argv[])
> +{
> +  enum
> +  {
> +    OPT_HELP = 256,
> +    OPT_VERSION,
> +  };
> +  static const struct option options[] = {
> +    { "help", no_argument, NULL, OPT_HELP },
> +    { "version", no_argument, NULL, OPT_VERSION },
> +  };
> +  int ret;
> +
> +  while ((ret = getopt_long (argc, argv, "hv", options, NULL)) != -1)
> +    {
> +      switch (ret)
> +        {
> +        case OPT_HELP:
> +        case 'h':
> +          pokefmt_help ();
> +          exit (EXIT_SUCCESS);
> +          break;
> +        case OPT_VERSION:
> +        case 'v':
> +          pokefmt_version ();
> +          exit (EXIT_SUCCESS);
> +          break;
> +        default:
> +          pokefmt_help ();
> +          exit (EXIT_FAILURE);
> +        }
> +    }
> +
> +  if (optind < argc)
> +    {
> +      pokefmt_opts.n_codes = argc - optind;
> +      pokefmt_opts.codes = malloc (pokefmt_opts.n_codes * sizeof (char *));
> +      if (pokefmt_opts.codes == NULL)
> +        err (1, "malloc() failed");
> +
> +      for (size_t i = 0; i < pokefmt_opts.n_codes; ++i)
> +        {
> +          char *p;
> +
> +          if (asprintf (&p, "%s;", argv[optind++]) == -1)
> +            err (1, "asprintf() failed");
> +          pokefmt_opts.codes[i] = p;
> +        }
> +    }
> +}
> +
> +static void
> +pokefmt_opts_free ()
> +{
> +  for (size_t i = 0; i < pokefmt_opts.n_codes; ++i)
> +    free (pokefmt_opts.codes [i]);
> +  free (pokefmt_opts.codes);
> +  pokefmt_opts.n_codes = 0;
> +}
> +
> +//--- poke
> +
> +// terminal IO functions
> +static void
> +tif_flush (void)
> +{
> +  fflush (stdout);
> +}
> +static void
> +tif_puts (const char *s)
> +{
> +  printf ("%s", s);
> +}
> +static void
> +tif_printf (const char *fmt, ...)
> +{
> +  va_list ap;
> +
> +  va_start (ap, fmt);
> +  vprintf (fmt, ap);
> +  va_end (ap);
> +}
> +static void
> +tif_indent (unsigned int level, unsigned int step)
> +{
> +  putchar ('\n');
> +  for (unsigned int i = 0; i < step * level; ++i)
> +    putchar (' ');
> +}
> +static void
> +tif_class (const char *name)
> +{
> +  (void)name;
> +}
> +static int
> +tif_class_end (const char *name)
> +{
> +  (void)name;
> +  return 1;
> +}
> +static void
> +tif_hlink (const char *name, const char *id)
> +{
> +  (void)name;
> +  (void)id;
> +}
> +static int
> +tif_hlink_end (void)
> +{
> +  return 1;
> +}
> +static struct pk_color
> +tif_color (void)
> +{
> +  static struct pk_color c = {
> +    .red = 0,
> +    .green = 0,
> +    .blue = 0,
> +  };
> +  return c;
> +}
> +static struct pk_color
> +tif_bgcolor (void)
> +{
> +  static struct pk_color c = {
> +    .red = 255,
> +    .green = 255,
> +    .blue = 255,
> +  };
> +  return c;
> +}
> +static void
> +tif_color_set (struct pk_color c)
> +{
> +  (void)c;
> +}
> +static void
> +tif_bgcolor_set (struct pk_color c)
> +{
> +  (void)c;
> +}
> +
> +static void
> +default_exception_handler (struct poke *poke, pk_val exc);
> +
> +void
> +poke_init (struct poke *pk, const char *poke_src_file, FILE *output)
> +{
> +  static struct pk_term_if tif = {
> +    .flush_fn = tif_flush,
> +    .puts_fn = tif_puts,
> +    .printf_fn = tif_printf,
> +    .indent_fn = tif_indent,
> +    .class_fn = tif_class,
> +    .end_class_fn = tif_class_end,
> +    .hyperlink_fn = tif_hlink,
> +    .end_hyperlink_fn = tif_hlink_end,
> +    .get_color_fn = tif_color,
> +    .get_bgcolor_fn = tif_bgcolor,
> +    .set_color_fn = tif_color_set,
> +    .set_bgcolor_fn = tif_bgcolor_set,
> +  };
> +  int ret;
> +  pk_val pexc;
> +
> +  pk->filename = strdup ("<stdin>");
> +  pk->output = output;
> +  pk->compiler = pk_compiler_new (&tif);
> +  if (pk->compiler == NULL)
> +    errx (1, "pk_compiler_new() failed");
> +
> +  /* Add load paths to the incremental compiler.  */
> +  {
> +    pk_val load_path = pk_decl_val (pk->compiler, "load_path");
> +    const char *poke_datadir = getenv ("POKEDATADIR");
> +    const char *poke_picklesdir = getenv ("POKEPICKLESDIR");
> +    const char *poke_load_path = getenv ("POKE_LOAD_PATH");
> +    char *pokefmt_appdir = getenv ("POKEFMTAPPDIR");
> +    char *user_load_path = NULL;
> +    char *new_path;
> +
> +    if (poke_datadir == NULL)
> +      poke_datadir = PKGDATADIR;
> +    if (pokefmt_appdir == NULL)
> +      pokefmt_appdir = "%DATADIR%/pokefmt";
> +    if (poke_picklesdir == NULL)
> +      poke_picklesdir = "%DATADIR%/pickles";
> +    if (poke_load_path)
> +      {
> +        user_load_path = pk_str_concat (poke_load_path, ":", NULL);
> +        if (user_load_path == NULL)
> +          err (1, "pk_str_concat() failed");
> +      }
> +
> +    new_path = pk_str_concat (user_load_path ? user_load_path : "",
> +                              pk_string_str (load_path), ":", pokefmt_appdir,
> +                              ":", poke_picklesdir, NULL);
> +    if (new_path == NULL)
> +      err (1, "pk_str_concat() failed");
> +    pk_decl_set_val (pk->compiler, "load_path", pk_make_string (new_path));
> +
> +    free (new_path);
> +    free (user_load_path);
> +  }
> +
> +  if (pk_load (pk->compiler, "pokefmt") != PK_OK)
> +    errx (1, "pk_load() failed for pokefmt.pk");
> +
> +  if (pk_decl_val (pk->compiler, "pokefmt_expr_printer") == PK_NULL)
> +    errx (1, "pokefmt_expr_printer Poke function is not available");
> +  if (pk_decl_val (pk->compiler, "pokefmt_exception_handler") == PK_NULL)
> +    errx (1, "pokefmt_exception_handler Poke function is not available");
> +
> +  /* Run user-provided Poke code (using command line args).  */
> +  for (size_t i = 0; i < pokefmt_opts.n_codes; ++i)
> +    {
> +      ret = pk_compile_buffer (pk->compiler, pokefmt_opts.codes[i], NULL,
> +                               &pexc);
> +      if (ret != PK_OK)
> +        errx (1,
> +              "pk_compile_buffer() failed for Poke code %zu on command line "
> +              "(%s)",
> +              i, pokefmt_opts.codes[i]);
> +      else if (pexc != PK_NULL)
> +        {
> +          default_exception_handler (pk, pexc);
> +          errx (1,
> +                "unhandled exception while running Poke code %zu on command "
> +                "line (%s)",
> +                i, pokefmt_opts.codes[i]);
> +        }
> +    }
> +}
> +
> +void
> +poke_free (struct poke *pk)
> +{
> +  free (pk->filename);
> +  pk_compiler_free (pk->compiler);
> +}
> +
> +static void
> +default_exception_handler (struct poke *poke, pk_val exc)
> +{
> +  pk_val handler = pk_decl_val (poke->compiler, "pokefmt_exception_handler");
> +
> +  assert (handler != PK_NULL);
> +  if (pk_call (poke->compiler, handler, NULL, NULL, 1, exc) != PK_OK)
> +    PK_UNREACHABLE ();
> +}
> +
> +static void
> +poke_stmt (struct poke *poke, const char *poke_src,
> +           const struct source_range *r)
> +{
> +  int res;
> +  pk_val exc;
> +
> +  assert (poke_src != NULL);
> +
> +  res = pk_compile_buffer_with_loc (
> +      poke->compiler, poke_src, poke->filename, r->line[0],
> +      r->col[0] + /* Skip "%{" or "%(". */ 2, NULL, &exc);
> +  if (res != PK_OK)
> +    errx (
> +        1,
> +        "pk_compile_buffer() failed for Poke code in range %d,%d-%d,%d: 
> '%s'",
> +        r->line[0], r->col[0], r->line[1], r->col[1], poke_src);
> +  else if (exc != PK_NULL)
> +    {
> +      default_exception_handler (poke, exc);
> +      errx (
> +          1,
> +          "unhandled exception happend during execution of Poke code in 
> range"
> +          " %d,%d-%d,%d: '%s'",
> +          r->line[0], r->col[0], r->line[1], r->col[1], poke_src);
> +    }
> +}
> +
> +static void
> +poke_expr (struct poke *poke, const char *poke_src,
> +           const struct source_range *r)
> +{
> +  int res;
> +  pk_val val, exc, printer, printer_ret, printer_exc;
> +
> +  assert (poke_src != NULL);
> +
> +  res = pk_compile_statement_with_loc (
> +      poke->compiler, poke_src, poke->filename, r->line[0],
> +      r->col[0] + /* Skip "%{" or "%(". */ 2, NULL, &val, &exc);
> +  if (res != PK_OK)
> +    errx (1,
> +          "pk_compile_statement() failed for Poke expression in range "
> +          "%d,%d-%d,%d: '%s'",
> +          r->line[0], r->col[0], r->line[1], r->col[1], poke_src);
> +  else if (exc != PK_NULL)
> +    {
> +      default_exception_handler (poke, exc);
> +      errx (
> +          1,
> +          "unhandled exception happend during execution of Poke code in 
> range"
> +          " %d,%d-%d,%d: '%s'",
> +          r->line[0], r->col[0], r->line[1], r->col[1], poke_src);
> +    }
> +  else if (val == PK_NULL)
> +    errx (1,
> +          "expected a Poke expression, got a Poke statement in Poke code in "
> +          "range %d,%d-%d,%d: '%s'",
> +          r->line[0], r->col[0], r->line[1], r->col[1], poke_src);
> +
> +  printer = pk_decl_val (poke->compiler, "pokefmt_expr_printer");
> +  res = pk_call (poke->compiler, printer, &printer_ret, &printer_exc,
> +                 /*narg*/ 1, val);
> +  if (res != PK_OK || printer_exc != PK_NULL)
> +    {
> +      default_exception_handler (poke, printer_exc);
> +      errx (1,
> +            "pk_call() failed for pokefmt_expr_printer during printing "
> +            "the result of Poke expression in range %d,%d-%d,%d: '%s'",
> +            r->line[0], r->col[0], r->line[1], r->col[1], poke_src);
> +    }
> +}
> diff --git a/pokefmt/pokefmt.pk b/pokefmt/pokefmt.pk
> new file mode 100644
> index 00000000..c71a44ff
> --- /dev/null
> +++ b/pokefmt/pokefmt.pk
> @@ -0,0 +1,47 @@
> +/* pokefmt.pk - Poke support code for pokefmt application.  */
> +
> +/* Copyright (C) 2023 Mohammad-Reza Nabipoor.  */
> +
> +/* This program is free software: you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation, either version 3 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program.  If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +/* Printer for Poke expressions (Poke code between "%(" and ")%".
> +
> +   User can re-write this function to customize the expression printer.  */
> +
> +fun pokefmt_expr_printer = (any val) void:
> +{
> +  _pkl_print_any (val);
> +}
> +
> +/* Default exception handler.
> +
> +   User can re-write this function to customize the default exception
> +   handling.  */
> +
> +fun pokefmt_exception_handler = (Exception exc) void:
> +{
> +  if (exc.code != EC_exit && exc.code != EC_signal)
> +    {
> +      printf ("unhandled %<warning:%s exception%>\n",
> +              exc.name == "" ? "unknown" : exc.name);
> +
> +      if (exc.location != "" || exc.msg != "")
> +        {
> +          if (exc.location != "")
> +            print (exc.location + " ");
> +          print (exc.msg + "\n");
> +        }
> +    }
> +}
> diff --git a/run.in b/run.in
> index 83f2ebd7..1fe75f59 100644
> --- a/run.in
> +++ b/run.in
> @@ -36,6 +36,7 @@ POKECONFIGDIR=$b/libpoke
>  POKEINFODIR=$s/doc
>  POKEAPPDIR=$s/poke
>  POKEDAPPDIR=$s/poked
> +POKEFMTAPPDIR=$s/pokefmt
>  POKEPICKLESDIR=$s/pickles
>  POKEMAPSDIR=$s/maps
>  POKEDOCDIR=$s/doc
> diff --git a/testsuite/Makefile.am b/testsuite/Makefile.am
> index a3fa534a..3ba92732 100644
> --- a/testsuite/Makefile.am
> +++ b/testsuite/Makefile.am
> @@ -36,6 +36,7 @@ if HAVE_DEJAGNU
>            POKECMDSDIR="$(top_srcdir)/poke" \
>            POKEDOCDIR="$(top_builddir)/doc" \
>            POKE_LOAD_PATH="$(top_srcdir)/poke" \
> +          POKEFMTAPPDIR="$(top_srcdir)/pokefmt" \
>               $$runtest --tool $(DEJATOOL) --srcdir $${srcdir} --objdir 
> $(builddir) \
>                          SHELL="$(SHELL)" \
>                       $(RUNTESTFLAGS) || exit $$1; \
> @@ -2798,6 +2799,7 @@ EXTRA_DIST = \
>    poke.pktest/pktest-10.pk \
>    poke.pktest/pktest-11.pk \
>    poke.pktest/pktest-12.pk \
> +  poke.pokefmt/pokefmt.exp \
>    poke.pvm/pvm-insns-test.pk \
>    poke.repl/repl.exp \
>    poke.std/std.exp \
> diff --git a/testsuite/poke.pokefmt/pokefmt.exp 
> b/testsuite/poke.pokefmt/pokefmt.exp
> new file mode 100644
> index 00000000..cd03efb2
> --- /dev/null
> +++ b/testsuite/poke.pokefmt/pokefmt.exp
> @@ -0,0 +1,115 @@
> +# pokefmt.exp - Tests for pokefmt application.
> +#
> +#   Copyright (C) 2023 The poke authors
> +#
> +# This program is free software; you can redistribute it and/or modify
> +# it under the terms of the GNU General Public License as published by
> +# the Free Software Foundation; either version 3 of the License, or
> +# (at your option) any later version.
> +#
> +# This program is distributed in the hope that it will be useful,
> +# but WITHOUT ANY WARRANTY; without even the implied warranty of
> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> +# GNU General Public License for more details.
> +#
> +# You should have received a copy of the GNU General Public License
> +# along with this program; if not, see <http://www.gnu.org/licenses/>.
> +
> +load_lib standard.exp
> +load_lib dejagnu.exp
> +
> +global POKEFMT
> +if ![info exists POKEFMT] {
> +    set POKEFMT ${objdir}/../pokefmt/pokefmt
> +}
> +
> +set tmpdir /tmp
> +catch {
> +    set tmpdir $::env(TMPDIR)
> +}
> +set SUBDIR [file join $tmpdir pokefmt-data.[pid]]
> +if {! [file exists $SUBDIR]} {
> +    file mkdir $SUBDIR
> +}
> +
> +set timeout 3
> +
> +proc pokefmt_run {test input output} {
> +    global POKEFMT
> +    global SUBDIR
> +
> +    set fin $SUBDIR/input
> +    set fout $SUBDIR/output
> +
> +    set in [open $fin w]
> +    puts -nonewline $in $input
> +    close $in
> +
> +    exec $POKEFMT < $fin > $fout
> +
> +    set out [open $fout r]
> +    set actual_output [read $out]
> +    close $out
> +
> +    if {[string equal "$output" "$actual_output"]} {
> +        pass "pokefmt $test"
> +    } else {
> +        fail "pokefmt $test"
> +        verbose "expected:'$output' != actual:'$actual_output'" 1
> +    }
> +}
> +
> +pokefmt_run 1 "Hi" "Hi"
> +pokefmt_run 2 "Hi{}Bye" "Hi{}Bye"
> +pokefmt_run 3 "Hi}%Bye" "Hi}%Bye"
> +pokefmt_run 4 "Hi{}%Bye" "Hi{}%Bye"
> +pokefmt_run 5 "Hi\\%{}%Bye" "Hi%{}%Bye"
> +pokefmt_run 6 "Hi%{}%Bye" "HiBye"
> +pokefmt_run 7 "Hi%{Bye" "Hi"
> +pokefmt_run 8 "Hi()Bye" "Hi()Bye"
> +pokefmt_run 9 "Hi)%Bye" "Hi)%Bye"
> +pokefmt_run 10 "Hi()%Bye" "Hi()%Bye"
> +pokefmt_run 11 "Hi\\%()%Bye" "Hi%()%Bye"
> +pokefmt_run 12 "Hi%(0)%Bye" "Hi0Bye"
> +pokefmt_run 13 "Hi%(Bye" "Hi"
> +pokefmt_run 14 {Hi%{print "-";}%Bye} "Hi-Bye"
> +pokefmt_run 15 {Hi%("-")%Bye} {Hi"-"Bye}
> +pokefmt_run 16 "Hi%()Bye" "Hi"
> +pokefmt_run 17 "%(0xff)%" "255"
> +pokefmt_run 18 "%(1 + 3 + 4)% + %(1)% = %(1+3+4+1)%" "8 + 1 = 9"
> +
> +pokefmt_run 19 {
> +#as: -march=rv32i
> +#objdump: -dr
> +%{
> +  load riscv;
> +  fun pokefmt_expr_printer = (any i32) void:
> +  {
> +    printf ("%u32x", i32 as uint<32>);
> +  }
> +}%
> +.*:[    ]+file format .*
> +
> +
> +Disassembly of section .text:
> +
> +0+000 <target>:
> +#...
> +[       ]+40:[  ]+%(+(rv32_auipc :rd 0 :imm 0))%[  ]+auipc[        ]+zero,0x0
> +[       ]+44:[  ]+%(+(rv32_lw :rd 0 :rs1 0 :imm 0))%[  ]+lw[   
> ]+zero,0\(zero\) # 0 .*
> +} {
> +#as: -march=rv32i
> +#objdump: -dr
> +
> +.*:[    ]+file format .*
> +
> +
> +Disassembly of section .text:
> +
> +0+000 <target>:
> +#...
> +[       ]+40:[  ]+00000017[  ]+auipc[        ]+zero,0x0
> +[       ]+44:[  ]+00002003[  ]+lw[   ]+zero,0\(zero\) # 0 .*
> +}
> +
> +file delete -force $SUBDIR

This is OK for master.
But please add yourself to HACKING as the maintainer of the new
subsystem.

Thanks.



reply via email to

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