summaryrefslogtreecommitdiff
path: root/mh
diff options
context:
space:
mode:
authorSergey Poznyakoff <gray@gnu.org>2017-10-16 10:05:58 +0300
committerSergey Poznyakoff <gray@gnu.org>2017-10-16 10:05:58 +0300
commit64d4a1b0d7ddba62ff8f72bf6e80cba58e868e82 (patch)
tree18ddcf644d701eed79ba556b2607f98a672b99e8 /mh
parent9c45fbd40c00a8e5d63c5b47dbed7da6b216e19f (diff)
downloadmailutils-64d4a1b0d7ddba62ff8f72bf6e80cba58e868e82.tar.gz
mailutils-64d4a1b0d7ddba62ff8f72bf6e80cba58e868e82.tar.bz2
Reimplement control escapes in mh_format.
This patch makes sure that (1) the value of the "num" register is not altered by evaluating the condition expressions and remains visible in the conditional branches, and (2) the value of the control escape is 1 or 0 depending on whether the last control condition succeeded or not. * mh/mh_format.h: Implement numeric stack in the vm. (mhop_pushn,mhop_popn,mhop_xchgn): New opcodes. (mh_fvm) <numstack,maxstack,tos>: New members. * mh/mh_fmtgram.y (ctx_stack): Reallocate as necessary. (yylex_cond): Handle backslash continuations. (format_parse): Initialize and free ctx_stack (codegen_node): Reimplement control escapes. See the comment for explanation * mh/mh_format.c (mh_fvm_run): Use mh_string_init to initialize registers. Initialize stack. Implement push, popn, and xchgn (extract_labels,mh_format_dump_disass): Handle new opcodes. * mh/tests/fmtcnd00.at: New testcase. * mh/tests/Makefile.am: Add new testcase * mh/tests/testsuite.at: Likewise. * mh/tests/fmtcomp.at: Fix expected code dumps
Diffstat (limited to 'mh')
-rw-r--r--mh/mh_fmtgram.y81
-rw-r--r--mh/mh_format.c47
-rw-r--r--mh/mh_format.h16
-rw-r--r--mh/tests/Makefile.am1
-rw-r--r--mh/tests/fmtcnd00.at109
-rw-r--r--mh/tests/fmtcomp.at41
-rw-r--r--mh/tests/testsuite.at2
7 files changed, 271 insertions, 26 deletions
diff --git a/mh/mh_fmtgram.y b/mh/mh_fmtgram.y
index b1ff63e5c..70b7d4860 100644
--- a/mh/mh_fmtgram.y
+++ b/mh/mh_fmtgram.y
@@ -35,17 +35,15 @@ enum context
ctx_func, /* after (func */
};
-static enum context ctx_stack[512];
+static enum context *ctx_stack;
size_t ctx_tos;
-
+size_t ctx_max;
+
static inline void
ctx_push (enum context ctx)
{
- if (ctx_tos == MU_ARRAY_SIZE (ctx_stack))
- {
- yyerror ("context nesting level too deep");
- exit (1);
- }
+ if (ctx_tos == ctx_max)
+ ctx_stack = mu_2nrealloc (ctx_stack, &ctx_max, sizeof (ctx_stack[0]));
ctx_stack[ctx_tos++] = ctx;
}
@@ -786,14 +784,21 @@ yylex_initial (void)
int
yylex_cond (void)
{
- switch (peek ())
+ while (1)
{
- case '(':
- return token_function ();
- case '{':
- return token_component ();
- default:
- return bogus ("'(' or '{' expected");
+ switch (peek ())
+ {
+ case '(':
+ return token_function ();
+ case '{':
+ return token_component ();
+ case '\\':
+ input ();
+ if (input () == '\n')
+ continue;
+ default:
+ return bogus ("'(' or '{' expected");
+ }
}
}
@@ -929,7 +934,8 @@ format_parse (mh_format_t *fmtptr, char *format_str,
start = tok_start = curp = format_str;
mu_opool_create (&tokpool, MU_OPOOL_ENOMEMABRT);
- ctx_tos = 0;
+ ctx_tos = ctx_max = 0;
+ ctx_stack = NULL;
ctx_push (ctx_init);
mu_linetrack_create (&trk, "input", 2);
if (locus && locus->mu_file)
@@ -944,6 +950,7 @@ format_parse (mh_format_t *fmtptr, char *format_str,
mu_locus_range_deinit (&yylloc);
mu_linetrack_destroy (&trk);
+ free (ctx_stack);
parse_tree = NULL;
tokpool = NULL;
@@ -1557,6 +1564,41 @@ codegen_node (struct mh_format *fmt, struct node *node)
{
long pc[2];
+ /* Implementation of control escapes is a bit tricky. According to
+ the spec:
+
+ "[f]unction escapes write their return value in 'num' for
+ functions returning integer or boolean values"
+
+ That means that after "%<(gt 1024)" the value of 'num' would be
+ 1 or 0, depending on its value prior to entering the conditional.
+ However this would defeat the purpose of the conditional itself,
+ because then the following construct would be meaningless:
+
+ %<(gt 1024)...%?(gt 512)...%|...%>
+
+ Indeed, in MH implementation the value of 'num' propagates into
+ the conditional expression, because any function escape serving
+ as condition is evaluated in a separate context.
+
+ To ensure this behavior, the virtual machine of GNU MH holds the
+ value of the 'num' register on stack while evaluating the condition
+ and restores it afterward.
+
+ On the other hand, the spec says that:
+
+ "[c]ontrol escapes return a boolean value, setting num to 1
+ if the last explicit condition evaluated by a `%<' or `%?'
+ control succeeded, and 0 otherwise."
+
+ To ensure this, the value on top of stack is exchanged with the
+ value of the 'num' register upon entering the 'if' branch, and
+ the tos value is popped into the 'num' upon leaving it. Any
+ 'else if' branches are handled the same way.
+
+ Before leaving the 'else' branch, the 'num' is set to 0 explicitly.
+ */
+ emit_opcode (fmt, mhop_pushn);
codegen_node (fmt, node->v.cntl.cond);
emit_opcode_typed (fmt, node->v.cntl.cond->datatype,
mhop_brzn, mhop_brzs);
@@ -1564,8 +1606,10 @@ codegen_node (struct mh_format *fmt, struct node *node)
emit_instr (fmt, (mh_instr_t) NULL);
if (node->v.cntl.iftrue)
{
+ emit_opcode (fmt, mhop_xchgn);
codegen_nodelist (fmt, node->v.cntl.iftrue);
}
+ emit_opcode (fmt, mhop_popn);
if (node->v.cntl.iffalse)
{
@@ -1574,7 +1618,14 @@ codegen_node (struct mh_format *fmt, struct node *node)
emit_instr (fmt, (mh_instr_t) NULL);
fmt->prog[pc[0]].num = fmt->progcnt - pc[0];
+ emit_opcode (fmt, mhop_popn);
codegen_nodelist (fmt, node->v.cntl.iffalse);
+ if (node->v.cntl.iffalse->nodetype != fmtnode_cntl)
+ {
+ emit_opcode (fmt, mhop_setn);
+ emit_instr (fmt, (mh_instr_t) (long) R_REG);
+ emit_instr (fmt, (mh_instr_t) (long) 0);
+ }
fmt->prog[pc[1]].num = fmt->progcnt - pc[1];
}
else
diff --git a/mh/mh_format.c b/mh/mh_format.c
index 92b8e2f07..88ee43525 100644
--- a/mh/mh_format.c
+++ b/mh/mh_format.c
@@ -24,6 +24,7 @@
#include <string.h>
#include <ctype.h>
+#include <assert.h>
#include "mbiter.h"
#include "mbchar.h"
#include "mbswidth.h"
@@ -480,13 +481,16 @@ mh_fvm_run (mh_fvm_t mach, mu_message_t msg)
reset_fmt_defaults (mach);
mu_list_clear (mach->addrlist);
- mh_string_clear (&mach->str[R_REG]);
- mh_string_clear (&mach->str[R_ARG]);
- mh_string_clear (&mach->str[R_ACC]);
+ mh_string_init (&mach->str[R_REG]);
+ mh_string_init (&mach->str[R_ARG]);
+ mh_string_init (&mach->str[R_ACC]);
mach->pc = 1;
mach->stop = 0;
mach->ind = 0;
+ mach->tos = 0;
+ mach->maxstack = 0;
+ mach->numstack = 0;
while (!mach->stop)
{
mh_opcode_t opcode;
@@ -558,6 +562,27 @@ mh_fvm_run (mh_fvm_t mach, mu_message_t msg)
/* FIXME: perhaps copy, not move? */
}
break;
+
+ case mhop_pushn:
+ if (mach->tos == mach->maxstack)
+ mach->numstack = mu_2nrealloc (mach->numstack, &mach->maxstack,
+ sizeof (mach->numstack[0]));
+ mach->numstack[mach->tos++] = mach->num[R_REG];
+ break;
+
+ case mhop_popn:
+ assert (mach->tos > 0);
+ mach->num[R_REG] = mach->numstack[--mach->tos];
+ break;
+
+ case mhop_xchgn:
+ assert (mach->tos > 0);
+ {
+ long t = mach->numstack[mach->tos-1];
+ mach->numstack[mach->tos-1] = mach->num[R_REG];
+ mach->num[R_REG] = t;
+ }
+ break;
case mhop_ldcomp:
{
@@ -2007,6 +2032,9 @@ extract_labels (mh_format_t fmt)
case mhop_itoa:
case mhop_printn:
case mhop_prints:
+ case mhop_pushn:
+ case mhop_popn:
+ case mhop_xchgn:
break;
default:
@@ -2159,6 +2187,18 @@ mh_format_dump_disass (mh_format_t fmt, int addr)
}
break;
+ case mhop_pushn:
+ printf ("pushn");
+ break;
+
+ case mhop_popn:
+ printf ("popn");
+ break;
+
+ case mhop_xchgn:
+ printf ("xchgn");
+ break;
+
case mhop_ldcomp:
{
long reg = MHI_NUM (prog[pc++]);
@@ -2282,6 +2322,7 @@ mh_fvm_destroy (mh_fvm_t *fvmp)
mh_fvm_t fvm = *fvmp;
free (fvm->prog);
+ free (fvm->numstack);
mh_string_free (&fvm->str[R_REG]);
mh_string_free (&fvm->str[R_ARG]);
mh_string_free (&fvm->str[R_ACC]);
diff --git a/mh/mh_format.h b/mh/mh_format.h
index 07fc6feb2..e2057dae6 100644
--- a/mh/mh_format.h
+++ b/mh/mh_format.h
@@ -85,9 +85,17 @@ enum mh_opcode
/* Set format specification.
Format: mhop_fmtspec number */
mhop_fmtspec,
+
+ /* Push numeric register */
+ mhop_pushn,
+ /* Pop numeric register */
+ mhop_popn,
+ /* Exchange top of stack value and numeric register */
+ mhop_xchgn,
};
enum regid { R_REG, R_ARG, R_ACC };
+#define MH_NREG 3
enum mh_type
{
@@ -158,8 +166,12 @@ struct mh_string
struct mh_fvm
{
- long num[2]; /* numeric registers */
- struct mh_string str[3]; /* string registers */
+ long num[MH_NREG]; /* numeric registers */
+ struct mh_string str[MH_NREG]; /* string registers */
+
+ long *numstack; /* Stack of numeric value */
+ size_t maxstack; /* Stack capacity */
+ size_t tos; /* Top of stack (next free slot) */
size_t pc; /* Program counter */
size_t progcnt; /* Size of allocated program*/
diff --git a/mh/tests/Makefile.am b/mh/tests/Makefile.am
index df74d2089..d01d7f278 100644
--- a/mh/tests/Makefile.am
+++ b/mh/tests/Makefile.am
@@ -51,6 +51,7 @@ TESTSUITE_AT = \
burst.at\
comp.at\
fmtcomp.at\
+ fmtcnd00.at\
fmtfunc.at\
folder.at\
forw.at\
diff --git a/mh/tests/fmtcnd00.at b/mh/tests/fmtcnd00.at
new file mode 100644
index 000000000..507e8e548
--- /dev/null
+++ b/mh/tests/fmtcnd00.at
@@ -0,0 +1,109 @@
+# This file is part of GNU Mailutils. -*- Autotest -*-
+# Copyright (C) 2010-2012, 2014-2017 Free Software Foundation, Inc.
+#
+# GNU Mailutils 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, or (at
+# your option) any later version.
+#
+# GNU Mailutils 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 GNU Mailutils. If not, see <http://www.gnu.org/licenses/>.
+
+AT_BANNER()
+
+m4_pushdef([FORMAT],
+[%(void(size))%<(gt 1000)%(divide 1000)m%?dnl
+(gt 100)%(divide 100)c%|dnl
+%(size)%> %(putnum)])
+
+dnl ------------------------------------------------------------
+dnl fmtcond([NAME], [INPUT], [STDOUT = `'])
+dnl
+m4_pushdef([FMTCOND],[
+AT_SETUP([$1])
+AT_KEYWORDS([format fmtcond fmtcnd00])
+AT_DATA([msg],[$2])
+AT_CHECK([fmtcheck -format 'FORMAT' msg
+],
+[0],
+[$3])
+AT_CLEANUP])
+
+FMTCOND(if branch,
+[From: gray@gnu.org
+To: foo@example.org
+Subject: greater than one thousand
+
+greater than 1000 bytes
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
+CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
+DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD
+EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE
+FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG
+HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH
+IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII
+JJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJ
+KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK
+LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL
+MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
+NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN
+OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO
+PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP
+QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ
+RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR
+SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS
+TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT
+UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU
+VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV
+WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW
+XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY
+ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ
+],
+[1m 1
+])
+
+FMTCOND(elseif branch,
+[From: gray@gnu.org
+To: foo@example.org
+Subject: less than one thousand
+
+less than 1000 bytes
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
+CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
+DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD
+EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE
+FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG
+HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH
+IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII
+JJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJ
+KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK
+LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL
+MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
+NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN
+OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO
+PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP
+QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ
+],
+[9c 1
+])
+
+FMTCOND(else branch,
+[From: gray@gnu.org
+To: foo@example.org
+Subject: under one hundred
+
+less than 100 bytes
+],
+[87 0
+])
+
diff --git a/mh/tests/fmtcomp.at b/mh/tests/fmtcomp.at
index 5b4694c2a..72cd823b0 100644
--- a/mh/tests/fmtcomp.at
+++ b/mh/tests/fmtcomp.at
@@ -167,9 +167,12 @@ FMTCOMP([simple conditional],
[IF (COMPONENT.replied) THEN
PRINT("-")
FI
+ pushn
ldcomp reg, "replied"
brzs L1
+ xchgn
printlit "-"
+ popn
L1: stop
],[],[-format])
@@ -180,11 +183,16 @@ FMTCOMP([if-else],
ELSE
PRINT("+")
FI
+ pushn
ldcomp reg, "replied"
brzs L1
+ xchgn
printlit "-"
+ popn
branch L2
-L1: printlit "+"
+L1: popn
+ printlit "+"
+ setn reg, 0
L2: stop
])
@@ -199,15 +207,24 @@ ELSE
PRINT(" ")
FI
FI
+ pushn
ldcomp reg, "replied"
brzs L1
+ xchgn
printlit "-"
+ popn
branch L3
-L1: ldcomp reg, "encrypted"
+L1: popn
+ pushn
+ ldcomp reg, "encrypted"
brzs L2
+ xchgn
printlit "E"
+ popn
branch L3
-L2: printlit " "
+L2: popn
+ printlit " "
+ setn reg, 0
L3: stop
])
@@ -219,11 +236,15 @@ FMTCOMP([inline conditional],
[%(formataddr %<{reply-to}%|%{from}%>)],
[formataddr(IF (COMPONENT.reply-to) THEN; ; ELSE PRINT(COMPONENT.from); FI)
movs acc, reg
+ pushn
ldcomp reg, "reply-to"
brzs L1
+ popn
branch L2
-L1: ldcomp reg, "from"
+L1: popn
+ ldcomp reg, "from"
prints
+ setn reg, 0
L2: movs arg, reg
call formataddr
stop
@@ -233,10 +254,14 @@ FMTCOMP([inline conditional (2)],
[%(formataddr %<{reply-to}%|%(void{from})%>)],
[formataddr(IF (COMPONENT.reply-to) THEN; ; ELSE COMPONENT.from; FI)
movs acc, reg
+ pushn
ldcomp reg, "reply-to"
brzs L1
+ popn
branch L2
-L1: ldcomp reg, "from"
+L1: popn
+ ldcomp reg, "from"
+ setn reg, 0
L2: movs arg, reg
call formataddr
stop
@@ -248,10 +273,14 @@ FMTCOMP([statement list],
width()
putaddr("To: ")
movs acc, reg
+ pushn
ldcomp reg, "reply-to"
brzs L1
+ popn
branch L2
-L1: ldcomp reg, "from"
+L1: popn
+ ldcomp reg, "from"
+ setn reg, 0
L2: movs arg, reg
call formataddr
call width
diff --git a/mh/tests/testsuite.at b/mh/tests/testsuite.at
index c6820843c..17ee32aea 100644
--- a/mh/tests/testsuite.at
+++ b/mh/tests/testsuite.at
@@ -86,3 +86,5 @@ m4_include([repl.at])
m4_include([mhn.at])
m4_include([send.at])
m4_include([sortm.at])
+
+m4_include([fmtcnd00.at]) \ No newline at end of file

Return to:

Send suggestions and report system problems to the System administrator.