summaryrefslogtreecommitdiff
path: root/mh
diff options
context:
space:
mode:
authorSergey Poznyakoff <gray@gnu.org.ua>2017-06-26 17:29:02 +0300
committerSergey Poznyakoff <gray@gnu.org.ua>2017-06-26 18:02:26 +0300
commit1a0899d8c5dd8ba4e6a600fcf4d8f2b43bafca0e (patch)
treed6988a38578fa0f0972dfc4f9c37bd0770070c74 /mh
parent6878eb8044703a339cb948d94acb0e06102506f7 (diff)
downloadmailutils-1a0899d8c5dd8ba4e6a600fcf4d8f2b43bafca0e.tar.gz
mailutils-1a0899d8c5dd8ba4e6a600fcf4d8f2b43bafca0e.tar.bz2
Start rewriting MH format functions.
* mh/fmtcheck.c: New option -disassemble * mh/mh.h: Move format-related declarations to mh_format.h (mh_format_parse): Change signature. (mh_format): Change signature. (mh_format_dump_code) (mh_format_dump_disass): New protos. (mh_decode_2047): Change signature. * mh/mh_fmtgram.y: Rewrite from scratch. First, build a parse tree, then use it to generate code. * mh/mh_format.c: Rewrite from scratch. * mh/mh_format.h: Move format-related declarations from mh.h * mh/mh_init.c (mh_decode_2047): Text is const. * mh/mh_list.c: Fix calls to mh_format_parse and related objects.
Diffstat (limited to 'mh')
-rw-r--r--mh/fmtcheck.c17
-rw-r--r--mh/mh.h141
-rw-r--r--mh/mh_fmtgram.y1349
-rw-r--r--mh/mh_format.c1096
-rw-r--r--mh/mh_format.h156
-rw-r--r--mh/mh_init.c2
-rw-r--r--mh/mh_list.c10
7 files changed, 1630 insertions, 1141 deletions
diff --git a/mh/fmtcheck.c b/mh/fmtcheck.c
index 585eb8ce1..53284b602 100644
--- a/mh/fmtcheck.c
+++ b/mh/fmtcheck.c
@@ -25,6 +25,7 @@ static char args_doc[] = N_("[FILE]");
static char *format_str;
static mh_format_t format;
static int dump_option;
+static int disass_option;
static int debug_option;
static char *input_file;
static size_t width;
@@ -41,6 +42,9 @@ static struct mu_option options[] = {
{ "dump", 0, NULL, MU_OPTION_HIDDEN,
N_("dump the listing of compiled format code"),
mu_c_bool, &dump_option },
+ { "disassemble", 0, NULL, MU_OPTION_HIDDEN,
+ N_("dump disassembled format code"),
+ mu_c_bool, &disass_option },
{ "debug", 0, NULL, MU_OPTION_DEFAULT,
N_("enable parser debugging output"),
mu_c_bool, &debug_option },
@@ -58,11 +62,8 @@ static void
run (void)
{
mu_message_t msg = mh_file_to_message (NULL, input_file);
- char *output;
-
- mh_format (&format, msg, msgno, width, &output);
-
- mu_printf ("%s\n", output);
+ mh_format (format, msg, msgno, width, mu_strout);
+ mu_printf ("\n");
}
int
@@ -90,14 +91,16 @@ main (int argc, char **argv)
mu_error (_("Format string not specified"));
return 1;
}
- if (mh_format_parse (format_str, &format))
+ if (mh_format_parse (&format, format_str, MH_FMT_PARSE_TREE))
{
mu_error (_("Bad format string"));
exit (1);
}
if (dump_option)
- mh_format_dump (&format);
+ mh_format_dump_code (format);
+ if (disass_option)
+ mh_format_dump_disass (format);
if (input_file)
run ();
diff --git a/mh/mh.h b/mh/mh.h
index 9153d1c9e..8b21dbe0e 100644
--- a/mh/mh.h
+++ b/mh/mh.h
@@ -65,11 +65,6 @@
#include <mu_umaxtostr.h>
-#define MH_FMT_RALIGN 0x1000
-#define MH_FMT_ZEROPAD 0x2000
-#define MH_FMT_COMPWS 0x4000
-#define MH_WIDTH_MASK 0x0fff
-
#define MH_SEQUENCES_FILE ".mh_sequences"
#define MH_USER_PROFILE ".mh_profile"
#define MH_GLOBAL_PROFILE "mh-profile"
@@ -78,118 +73,6 @@
#define is_true(arg) ((arg) == NULL||mu_true_answer_p (arg) == 1)
-enum mh_opcode
-{
- /* 0. Stop. Format: mhop_stop */
- mhop_stop,
- /* 1. Branch.
- Format: mhop_branch offset */
- mhop_branch,
- /* 2. Assign to numeric register
- Format: mhop_num_asgn */
- mhop_num_asgn,
- /* 3. Assign to string register
- Format: mhop_str_asgn */
- mhop_str_asgn,
- /* 4. Numeric arg follows.
- Format: mhop_num_arg number */
- mhop_num_arg,
- /* 5. String arg follows.
- Format: mhop_str_arg length string */
- mhop_str_arg,
- /* 6. Branch if reg_num is zero.
- Format: mhop_num_branch dest-off */
- mhop_num_branch,
- /* 7. Branch if reg_str is zero.
- Format: mhop_str_branch dest-off */
- mhop_str_branch,
- /* 8. Set str to the value of the header component
- Format: mhop_header */
- mhop_header,
-
- /* 9. Read message body contents into str.
- Format: mhop_body */
- mhop_body,
-
- /* 10. Call a function.
- Format: mhop_call function-pointer */
- mhop_call,
-
- /* 11. assign reg_num to arg_num */
- mhop_num_to_arg,
-
- /* 12. assign reg_str to arg_str */
- mhop_str_to_arg,
-
- /* 13. Convert arg_str to arg_num */
- mhop_str_to_num,
-
- /* 14. Convert arg_num to arg_str */
- mhop_num_to_str,
-
- /* 15. Print reg_num */
- mhop_num_print,
-
- /* 16. Print reg_str */
- mhop_str_print,
-
- /* 17. Set format specification.
- Format: mhop_fmtspec number */
- mhop_fmtspec,
-
- /* 18. Noop */
- mhop_nop
-};
-
-enum mh_type
-{
- mhtype_none,
- mhtype_num,
- mhtype_str
-};
-
-typedef enum mh_opcode mh_opcode_t;
-
-struct mh_machine;
-typedef void (*mh_builtin_fp) (struct mh_machine *);
-
-typedef union {
- mh_opcode_t opcode;
- mh_builtin_fp builtin;
- int num;
- void *ptr;
- char str[1]; /* Any number of characters follows */
-} mh_instr_t;
-
-#define MHI_OPCODE(m) (m).opcode
-#define MHI_BUILTIN(m) (m).builtin
-#define MHI_NUM(m) (m).num
-#define MHI_PTR(m) (m).ptr
-#define MHI_STR(m) (m).str
-
-typedef struct mh_format mh_format_t;
-
-struct mh_format
-{
- size_t progsize; /* Size of allocated program*/
- mh_instr_t *prog; /* Program itself */
-};
-
-#define MHA_REQUIRED 0
-#define MHA_OPTARG 1
-#define MHA_OPT_CLEAR 2
-
-typedef struct mh_builtin mh_builtin_t;
-
-struct mh_builtin
-{
- char *name;
- mh_builtin_fp fun;
- int type;
- int argtype;
- int optarg;
-};
-
typedef struct
{
const char *name;
@@ -284,14 +167,22 @@ int mu_getans (const char *variants, const char *fmt, ...)
int mh_check_folder (const char *pathname, int confirm);
int mh_makedir (const char *p);
-int mh_format (mh_format_t *fmt, mu_message_t msg, size_t msgno,
- size_t width, char **pret);
-int mh_format_str (mh_format_t *fmt, char *str, size_t width, char **pret);
-void mh_format_dump (mh_format_t *fmt);
-int mh_format_parse (char *format_str, mh_format_t *fmt);
+typedef struct mh_format *mh_format_t;
+
+int mh_format (mh_format_t fmt, mu_message_t msg, size_t msgno,
+ size_t width, mu_stream_t str);
+int mh_format_str (mh_format_t fmt, char *str, size_t width, char **pret);
+
+void mh_format_dump_code (mh_format_t fmt);
+void mh_format_dump_disass (mh_format_t fmt);
+
+#define MH_FMT_PARSE_DEFAULT 0
+#define MH_FMT_PARSE_TREE 0x01
+int mh_format_parse (mh_format_t *fmt, char *format_str, int flags);
+
void mh_format_debug (int val);
-void mh_format_free (mh_format_t *fmt);
-mh_builtin_t *mh_lookup_builtin (char *name, int *rest);
+void mh_format_free (mh_format_t fmt);
+void mh_format_destroy (mh_format_t *fmt);
void mh_error (const char *fmt, ...) MU_PRINTFLIKE(1,2);
void mh_err_memory (int fatal);
@@ -350,7 +241,7 @@ int mh_whom_file (const char *filename, int check);
int mh_whom_message (mu_message_t msg, int check);
void mh_set_reply_regex (const char *str);
-int mh_decode_2047 (char *text, char **decoded_text);
+int mh_decode_2047 (char const *text, char **decoded_text);
const char *mh_charset (const char *);
int mh_alias_read (char const *name, int fail);
diff --git a/mh/mh_fmtgram.y b/mh/mh_fmtgram.y
index 070e60f69..4db1fbe6e 100644
--- a/mh/mh_fmtgram.y
+++ b/mh/mh_fmtgram.y
@@ -17,285 +17,316 @@
along with GNU Mailutils. If not, see <http://www.gnu.org/licenses/>. */
#include <mh.h>
+#include <mh_format.h>
int yyerror (const char *s);
-int yylex ();
+int yylex (void);
-static mh_format_t format; /* Format structure being built */
-static size_t pc; /* Program counter. Poins to current
- cell in format.prog */
static mu_opool_t tokpool; /* Temporary token storage */
-#define FORMAT_INC 64 /* Increase format.prog by that many
- cells each time pc reaches
- format.progsize */
+
+/* Lexical context */
+enum context
+ {
+ ctx_init, /* Normal text */
+ ctx_if, /* After %< or %? */
+ ctx_expr, /* Expression within cond */
+ ctx_func, /* after (func */
+ };
+
+static enum context ctx_stack[512];
+size_t ctx_tos;
+
+static inline void
+ctx_push (enum context ctx)
+{
+ if (ctx_tos == MU_ARRAY_SIZE (ctx_stack))
+ {
+ yyerror ("context nesting level too deep");
+ exit (1);
+ }
+ ctx_stack[ctx_tos++] = ctx;
+}
-static size_t mh_code_op (mh_opcode_t op);
-static size_t mh_code_string (char *string);
-static size_t mh_code_number (int num);
-static size_t mh_code_builtin (mh_builtin_t *bp, int argtype);
-static void branch_fixup (size_t pc, size_t tgt);
+static inline void
+ctx_pop (void)
+{
+ if (ctx_tos == 0)
+ {
+ yyerror ("out of context");
+ abort ();
+ }
+ ctx_tos--;
+}
+
+static inline enum context
+ctx_get (void)
+{
+ return ctx_stack[ctx_tos-1];
+}
+
+enum node_type
+{
+ fmtnode_print,
+ fmtnode_literal,
+ fmtnode_number,
+ fmtnode_body,
+ fmtnode_comp,
+ fmtnode_funcall,
+ fmtnode_cntl,
+ fmtnode_typecast
+};
- /* Lexical tie-ins */
-static int in_escape; /* Set when inside an escape sequence */
-static int want_function; /* Set when expecting function name */
-static int want_arg; /* Expecting function argument */
+struct node
+{
+ enum node_type nodetype;
+ enum mh_type datatype;
+ int noprint:1;
+ struct node *prev, *next;
+ union
+ {
+ char *str;
+ long num;
+ struct node *arg;
+ struct
+ {
+ int fmtspec;
+ struct node *arg;
+ } prt;
+ struct
+ {
+ mh_builtin_t *builtin;
+ struct node *arg;
+ } funcall;
+ struct
+ {
+ struct node *cond;
+ struct node *iftrue;
+ struct node *iffalse;
+ } cntl;
+ } v;
+};
+
+static struct node *parse_tree;
+static struct node *new_node (enum node_type nodetype, enum mh_type datatype);
+
+static struct node *printelim (struct node *root);
+static void codegen (mh_format_t *fmt, int tree);
+static struct node *typecast (struct node *node, enum mh_type type);
+
%}
%union {
char *str;
- int num;
- int type;
+ long num;
struct {
- size_t cond;
- size_t end;
- } elif_list;
- size_t pc;
+ struct node *head, *tail;
+ } nodelist;
+ struct node *nodeptr;
mh_builtin_t *builtin;
+ int fmtspec;
+ struct {
+ enum mh_type type;
+ union
+ {
+ char *str;
+ long num;
+ } v;
+ } arg;
};
+
%token <num> NUMBER
-%token <str> STRING
+%token <str> STRING COMPONENT
+%token <arg> ARGUMENT
%token <builtin> FUNCTION
%token IF ELIF ELSE FI
-%token OBRACE CBRACE OCURLY CCURLY
-%token <num> FMTSPEC
+%token <fmtspec> FMTSPEC
%token BOGUS
-%type <type> cond_expr component funcall item argument escape literal
-%type <elif_list> elif_part elif_list fi
-%type <pc> cond end else elif else_part zlist list pitem
+%token EOFN
+
+%type <nodelist> list zlist elif_list
+%type <nodeptr> item escape component funcall cntl argument
+%type <nodeptr> cond cond_expr elif_part else_part printable
%type <builtin> function
+%type <fmtspec> fmtspec
%%
input : list
{
- /* nothing: to shut bison up */
+ parse_tree = $1.head;
}
;
-list : pitem
- | list pitem
- ;
-
-pitem : item
+list : item
{
- switch ($1)
- {
- case mhtype_none:
- break;
-
- case mhtype_num:
- mh_code_op (mhop_num_asgn);
- mh_code_op (mhop_num_print);
- break;
-
- case mhtype_str:
- mh_code_op (mhop_str_asgn);
- mh_code_op (mhop_str_print);
- break;
-
- default:
- yyerror (_("INTERNAL ERROR: unexpected item type (please report)"));
- abort ();
- }
- $$ = pc;
+ $$.head = $$.tail = $1;
}
- ;
-
-item : literal
- | escape
- {
- in_escape = 0;
+ | list item
+ {
+ $2->prev = $1.tail;
+ $1.tail->next = $2;
+ $1.tail = $2;
+ $$ = $1;
}
;
-literal : STRING
+item : STRING
{
- mh_code_string ($1);
- $$ = mhtype_str;
- }
- | NUMBER
- {
- mh_code_number ($1);
- $$ = mhtype_num;
- }
- ;
-
-escape : component
- | funcall
- | cntl
- {
- $$ = mhtype_none;
+ struct node *n = new_node (fmtnode_literal, mhtype_str);
+ n->v.str = $1;
+ $$ = new_node (fmtnode_print, mhtype_str);
+ $$->v.prt.arg = n;
}
+ | escape
;
-component : fmtspec OCURLY STRING CCURLY
+escape : cntl
+ | fmtspec printable
{
- if (mu_c_strcasecmp ($3, "body") == 0)
- {
- mh_code_op (mhop_body);
- }
+ if ($2->noprint)
+ $$ = $2;
else
{
- mh_code_string ($3);
- mh_code_op (mhop_header);
+ $$ = new_node (fmtnode_print, $2->datatype);
+ $$->v.prt.fmtspec = $1;
+ $$->v.prt.arg = $2;
}
- $$ = mhtype_str;
}
;
-obrace : OBRACE
- {
- in_escape++;
- }
+printable : component
+ | funcall
;
-cbrace : CBRACE
+component : COMPONENT
{
- in_escape--;
+ if (mu_c_strcasecmp ($1, "body") == 0)
+ $$ = new_node (fmtnode_body, mhtype_str);
+ else
+ {
+ $$ = new_node (fmtnode_comp, mhtype_str);
+ $$->v.str = $1;
+ }
}
;
-funcall : fmtspec obrace { want_function = 1;} function { want_function = 0; want_arg = 1;} argument cbrace
+funcall : function argument EOFN
{
- if ($4)
+ ctx_pop ();
+ if ($1->optarg == MHA_VOID) /*FIXME*/
{
- if (!mh_code_builtin ($4, $6))
- YYERROR;
- $$ = $4->type;
+ $2->noprint = 1;
+ $$ = $2;
}
else
{
- switch ($6)
+ if ($1->argtype == mhtype_none)
{
- default:
- break;
-
- case mhtype_num:
- mh_code_op (mhop_num_asgn);
- break;
-
- case mhtype_str:
- mh_code_op (mhop_str_asgn);
- break;
+ if ($2)
+ {
+ yyerror ("function doesn't take arguments");
+ YYABORT;
+ }
}
- $$ = mhtype_none;
+ else if ($2 == NULL)
+ {
+ if ($1->optarg != MHA_OPTARG)
+ {
+ yyerror ("required argument missing");
+ YYABORT;
+ }
+ }
+ $$ = new_node (fmtnode_funcall, $1->type);
+ $$->v.funcall.builtin = $1;
+ $$->v.funcall.arg = typecast ($2, $1->argtype);
+ $$->noprint = $1->type == mhtype_none;
}
}
;
fmtspec : /* empty */
- | FMTSPEC
{
- mh_code_op (mhop_fmtspec);
- mh_code_op ($1);
+ $$ = 0;
}
+ | FMTSPEC
;
function : FUNCTION
- | STRING
{
- if (strcmp ($1, "void") == 0)
- {
- $$ = NULL;
- }
- else
- {
- yyerror (_("undefined function"));
- mu_error ("%s", $1);
- YYERROR;
- }
+ ctx_push (ctx_func);
}
;
argument : /* empty */
{
- $$ = mhtype_none;
+ $$ = NULL;
}
- | literal
- | escape
- ;
-
-/* 1 2 3 4 5 6 7 */
-cntl : if cond zlist end elif_part else_part fi
+ | ARGUMENT
{
- size_t start_pc = 0, end_pc = 0;
-
- /* Fixup first condition */
- if ($5.cond)
- MHI_NUM(format.prog[$2]) = $5.cond - $2;
- else if ($6)
- MHI_NUM(format.prog[$2]) = $6 - $2;
- else
- MHI_NUM(format.prog[$2]) = $7.cond - $2;
-
- /* Link all "false" lists */
- if ($5.cond)
+ switch ($1.type)
{
- start_pc = $5.end;
- end_pc = $5.end;
- while (MHI_NUM(format.prog[end_pc]))
- end_pc = MHI_NUM(format.prog[end_pc]);
- }
+ case mhtype_none:
+ $$ = NULL;
+ break;
+
+ case mhtype_str:
+ $$ = new_node (fmtnode_literal, mhtype_str);
+ $$->v.str = $1.v.str;
+ break;
- if (start_pc)
- MHI_NUM(format.prog[end_pc]) = $4;
- else
- start_pc = $4;
+ case mhtype_num:
+ $$ = new_node (fmtnode_number, mhtype_num);
+ $$->v.num = $1.v.num;
+ }
+ }
+ | escape
+ {
+ $$ = printelim ($1);
+ }
+ ;
- /* Now, fixup the end branches */
- branch_fixup (start_pc, $7.end);
- MHI_NUM(format.prog[start_pc]) = $7.end - start_pc;
+/* 1 2 3 4 5 */
+cntl : if cond zlist elif_part fi
+ {
+ $$ = new_node(fmtnode_cntl, mhtype_num);
+ $$->v.cntl.cond = $2;
+ $$->v.cntl.iftrue = $3.head;
+ $$->v.cntl.iffalse = $4;
}
;
zlist : /* empty */
{
- $$ = pc;
+ $$.head = $$.tail = NULL;
}
| list
;
if : IF
{
- in_escape++;
+ ctx_push (ctx_if);
}
;
fi : FI
{
- /* False branch of an if-block */
- $$.cond = mh_code_op (mhop_num_asgn);
- /* Jump over the true branch */
- mh_code_op (mhop_branch);
- mh_code_op (2);
- /* True branch */
- $$.end = mh_code_op (mhop_num_asgn);
+ ctx_pop ();
}
;
elif : ELIF
{
- in_escape++;
- $$ = pc;
- }
- ;
-
-end : /* empty */
- {
- mh_code_op (mhop_branch);
- $$ = mh_code_op (0);
+ ctx_pop ();
+ ctx_push (ctx_if);
}
;
cond : cond_expr
{
- in_escape--;
- if ($1 == mhtype_str)
- mh_code_op (mhop_str_branch);
- else
- mh_code_op (mhop_num_branch);
- $$ = mh_code_op (0);
+ ctx_pop ();
+ ctx_push (ctx_expr);
+ $$ = printelim ($1);
}
;
@@ -305,45 +336,49 @@ cond_expr : component
elif_part : /* empty */
{
- $$.cond = 0;
- $$.end = 0;
+ $$ = NULL;
}
- | elif_list end
- {
- $$.cond = $1.cond;
- MHI_NUM(format.prog[$2]) = $1.end;
- $$.end = $2;
+ | else_part
+ | elif_list
+ {
+ $$ = $1.head;
}
;
elif_list : elif cond zlist
{
- $$.cond = $1;
- MHI_NUM(format.prog[$2]) = pc - $2 + 2;
- $$.end = 0;
+ struct node *np = new_node (fmtnode_cntl, mhtype_num);
+ np->v.cntl.cond = $2;
+ np->v.cntl.iftrue = $3.head;
+ np->v.cntl.iffalse = NULL;
+ $$.head = $$.tail = np;
}
- | elif_list end elif cond zlist
- {
- MHI_NUM(format.prog[$4]) = pc - $4 + 2;
- $$.cond = $1.cond;
- MHI_NUM(format.prog[$2]) = $1.end;
- $$.end = $2;
+ | elif_list elif cond zlist
+ {
+ struct node *np = new_node(fmtnode_cntl, mhtype_num);
+ np->v.cntl.cond = $3;
+ np->v.cntl.iftrue = $4.head;
+ np->v.cntl.iffalse = NULL;
+
+ $1.tail->v.cntl.iffalse = np;
+ $1.tail = np;
+
+ $$ = $1;
}
+ | elif_list else_part
+ {
+ $1.tail->v.cntl.iffalse = $2;
+ $1.tail = $2;
+ $$ = $1;
+ }
;
-else_part : /* empty */
- {
- $$ = 0;
+else_part : ELSE list
+ {
+ $$ = $2.head;
}
- | else list
;
-else : ELSE
- {
- $$ = pc;
- }
- ;
-
%%
static char *start;
@@ -352,104 +387,154 @@ static char *curp;
int
yyerror (const char *s)
{
- int len;
- mu_error ("%s: %s", start, s);
- len = curp - start;
- mu_error ("%*.*s^", len, len, "");
+ if (yychar != BOGUS)
+ {
+ int len;
+ mu_error ("%s: %s", start, s);
+ len = curp - start;
+ mu_error ("%*.*s^", len, len, "");
+ }
return 0;
}
-#define isdelim(c) (strchr("%<>?|(){} ",c) != NULL)
-
-static int percent;
static int backslash(int c);
-
-int
-yylex ()
+
+struct lexer_tab
{
- /* Reset the tie-in */
- int expect_arg = want_arg;
- want_arg = 0;
+ char *ctx_name;
+ int (*lexer) (void);
+};
+
+static int yylex_initial (void);
+static int yylex_cond (void);
+static int yylex_expr (void);
+static int yylex_func (void);
+
+static struct lexer_tab lexer_tab[] = {
+ [ctx_init] = { "initial", yylex_initial },
+ [ctx_if] = { "condition", yylex_cond },
+ [ctx_expr] = { "expression", yylex_expr },
+ [ctx_func] = { "function", yylex_func }
+};
+int
+yylex (void)
+{
if (yydebug)
- fprintf (stderr, "[lex at %10.10s]\n", curp);
- if (*curp == '%')
+ fprintf (stderr, "lex: [%s] at %-10.10s...]\n",
+ lexer_tab[ctx_get ()].ctx_name, curp);
+ return lexer_tab[ctx_get ()].lexer ();
+}
+
+static int
+token_fmtspec (int flags)
+{
+ int num = 0;
+
+ if (*curp == '0')
{
+ flags |= MH_FMT_ZEROPAD;
curp++;
- percent = 1;
- if (mu_isdigit (*curp) || *curp == '-')
- {
- int num = 0;
- int flags = 0;
+ }
+ else if (!mu_isdigit (*curp))
+ {
+ yyerror ("expected digit");
+ return BOGUS;
+ }
+
+ while (*curp && mu_isdigit (*curp))
+ num = num * 10 + *curp++ - '0';
+ yylval.fmtspec = flags | num;
+ *--curp = '%'; /* FIXME: dirty hack */
+ return FMTSPEC;
+}
- if (*curp == '-')
- {
- curp++;
- flags = MH_FMT_RALIGN;
- }
- if (*curp == '0')
- flags |= MH_FMT_ZEROPAD;
- while (*curp && mu_isdigit (*curp))
- num = num * 10 + *curp++ - '0';
- yylval.num = num | flags;
- return FMTSPEC;
- }
+static int
+token_function (void)
+{
+ char *start;
+
+ curp++;
+ start = curp;
+ curp = mu_str_skip_class (start, MU_CTYPE_ALPHA);
+ if (start == curp || !strchr (" \t(){%", *curp))
+ {
+ yyerror ("expected function name");
+ return BOGUS;
}
- if (percent)
+ yylval.builtin = mh_lookup_builtin (start, curp - start);
+
+ if (!yylval.builtin)
{
- percent = 0;
- switch (*curp++)
+ yyerror ("unknown function");
+ return BOGUS;
+ }
+
+ return FUNCTION;
+}
+
+static int
+token_component (void)
+{
+ char *start;
+
+ curp++;
+ if (!mu_isalpha (*curp))
+ {
+ yyerror ("component name expected");
+ return BOGUS;
+ }
+ start = curp;
+ for (; *curp != '}'; curp++)
+ {
+ if (!(mu_isalnum (*curp) || *curp == '_' || *curp == '-'))
+ {
+ yyerror ("component name expected");
+ return BOGUS;
+ }
+ }
+ mu_opool_append (tokpool, start, curp - start);
+ mu_opool_append_char (tokpool, 0);
+ yylval.str = mu_opool_finish (tokpool, NULL);
+ curp++;
+ return COMPONENT;
+}
+
+int
+yylex_initial (void)
+{
+ if (*curp == '%')
+ {
+ int c;
+ curp++;
+
+ switch (c = *curp++)
{
case '<':
return IF;
- case '>':
- return FI;
- case '?':
- return ELIF;
- case '|':
- return ELSE;
case '%':
return '%';
case '(':
- return OBRACE;
+ curp--;
+ return token_function ();
case '{':
- return OCURLY;
+ curp--;
+ return token_component ();
+ case '-':
+ return token_fmtspec (MH_FMT_RALIGN);
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ curp--;
+ return token_fmtspec (MH_FMT_DEFAULT);
default:
+ yyerror ("component or function name expected");
return BOGUS;
}
}
- if (in_escape)
- {
- while (*curp && (*curp == ' ' || *curp == '\n'))
- curp++;
- switch (*curp)
- {
- case '(':
- curp++;
- return OBRACE;
- case '{':
- curp++;
- return OCURLY;
- case '0':case '1':case '2':case '3':case '4':
- case '5':case '6':case '7':case '8':case '9':
- yylval.num = strtol (curp, &curp, 0);
- return NUMBER;
- }
- }
-
- switch (*curp)
- {
- case ')':
- curp++;
- return CBRACE;
- case '}':
- curp++;
- return CCURLY;
- case 0:
- return 0;
- }
+ if (*curp == 0)
+ return 0;
do
{
@@ -462,28 +547,124 @@ yylex ()
mu_opool_append_char (tokpool, *curp);
curp++;
}
- while (*curp && (expect_arg ? *curp != ')' : !isdelim(*curp)));
+ while (*curp && *curp != '%');
mu_opool_append_char (tokpool, 0);
yylval.str = mu_opool_finish (tokpool, NULL);
+ return STRING;
+}
- if (want_function)
+int
+yylex_cond (void)
+{
+ switch (*curp)
{
- int rest;
- mh_builtin_t *bp = mh_lookup_builtin (yylval.str, &rest);
- if (bp)
+ case '(':
+ return token_function ();
+ case '{':
+ return token_component ();
+ default:
+ yyerror ("'(' or '{' expected");
+ return BOGUS;
+ }
+}
+
+int
+yylex_expr (void)
+{
+ if (*curp == '%')
+ {
+ curp++;
+ switch (*curp++)
{
- curp -= rest;
- yylval.builtin = bp;
- while (*curp && mu_isspace (*curp))
- curp++;
- return FUNCTION;
+ case '?':
+ return ELIF;
+ case '|':
+ return ELSE;
+ case '>':
+ return FI;
}
+ curp -= 2;
}
-
- return STRING;
+ return yylex_initial ();
}
+int
+yylex_func (void)
+{
+ /* Expected argument or closing parenthesis */
+ while (*curp && mu_isspace (*curp))
+ curp++;
+
+ switch (*curp)
+ {
+ case '(':
+ return token_function ();
+
+ case ')':
+ curp++;
+ return EOFN;
+
+ case '{':
+ return token_component ();
+
+ case '%':
+ curp++;
+ switch (*curp)
+ {
+ case '<':
+ curp++;
+ return IF;
+
+ case '%':
+ break;
+
+ default:
+ yyerror ("expected '%' or '<'");
+ return BOGUS;
+ }
+ }
+
+ if (mu_isdigit (*curp))
+ {
+ yylval.arg.type = mhtype_num;
+ yylval.arg.v.num = strtol (curp, &curp, 0);
+ }
+ else
+ {
+ do
+ {
+ if (*curp == 0)
+ {
+ yyerror("expected ')'");
+ return BOGUS;
+ }
+
+ if (*curp == '\\')
+ {
+ int c = backslash (*++curp);
+ mu_opool_append_char (tokpool, c);
+ }
+ else
+ mu_opool_append_char (tokpool, *curp);
+ curp++;
+ }
+ while (*curp != ')');
+ mu_opool_append_char (tokpool, 0);
+
+ yylval.arg.type = mhtype_str;
+ yylval.arg.v.str = mu_opool_finish (tokpool, NULL);
+ }
+
+ if (*curp != ')')
+ {
+ yyerror("expected ')'");
+ return BOGUS;
+ }
+
+ return ARGUMENT;
+}
+
void
mh_format_debug (int val)
{
@@ -491,37 +672,32 @@ mh_format_debug (int val)
}
int
-mh_format_parse (char *format_str, mh_format_t *fmt)
+mh_format_parse (mh_format_t *fmtptr, char *format_str, int flags)
{
int rc;
char *p = getenv ("MHFORMAT_DEBUG");
-
- if (p)
+
+ if (p || mu_debug_level_p (MU_DEBCAT_APP, MU_DEBUG_TRACE2))
yydebug = 1;
start = curp = format_str;
mu_opool_create (&tokpool, MU_OPOOL_ENOMEMABRT);
- format.prog = NULL;
- format.progsize = 0;
- pc = 0;
- mh_code_op (mhop_stop);
-
- in_escape = 0;
- percent = 0;
+ ctx_tos = 0;
+ ctx_push (ctx_init);
+
rc = yyparse ();
- mh_code_op (mhop_stop);
- mu_opool_destroy (&tokpool);
- if (rc)
- {
- mh_format_free (&format);
- return 1;
- }
- *fmt = format;
- return 0;
+ if (rc == 0)
+ codegen (fmtptr, flags & MH_FMT_PARSE_TREE);
+ else
+ mu_opool_destroy (&tokpool);
+
+ parse_tree = NULL;
+ tokpool = NULL;
+ return rc;
}
int
-backslash(int c)
+backslash (int c)
{
static char transtab[] = "b\bf\fn\nr\rt\t";
char *p;
@@ -534,131 +710,500 @@ backslash(int c)
return c;
}
+static struct node *
+new_node (enum node_type nodetype, enum mh_type datatype)
+{
+ struct node *np = mu_zalloc (sizeof *np);
+ np->nodetype = nodetype;
+ np->datatype = datatype;
+ return np;
+}
+
+static void node_list_free (struct node *node);
+
+static void
+node_free (struct node *node)
+{
+ switch (node->nodetype)
+ {
+ case fmtnode_print:
+ node_free (node->v.prt.arg);
+ break;
+
+ case fmtnode_literal:
+ break;
+
+ case fmtnode_number:
+ break;
+
+ case fmtnode_body:
+ break;
+
+ case fmtnode_comp:
+ break;
+
+ case fmtnode_funcall:
+ node_free (node->v.funcall.arg);
+ break;
+
+ case fmtnode_cntl:
+ node_list_free (node->v.cntl.cond);
+ node_list_free (node->v.cntl.iftrue);
+ node_list_free (node->v.cntl.iffalse);
+ break;
+
+ default:
+ abort ();
+ }