diff options
author | Sergey Poznyakoff <gray@gnu.org.ua> | 2017-06-26 17:29:02 +0300 |
---|---|---|
committer | Sergey Poznyakoff <gray@gnu.org.ua> | 2017-06-26 18:02:26 +0300 |
commit | 1a0899d8c5dd8ba4e6a600fcf4d8f2b43bafca0e (patch) | |
tree | d6988a38578fa0f0972dfc4f9c37bd0770070c74 /mh | |
parent | 6878eb8044703a339cb948d94acb0e06102506f7 (diff) | |
download | mailutils-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.c | 17 | ||||
-rw-r--r-- | mh/mh.h | 141 | ||||
-rw-r--r-- | mh/mh_fmtgram.y | 1349 | ||||
-rw-r--r-- | mh/mh_format.c | 1096 | ||||
-rw-r--r-- | mh/mh_format.h | 156 | ||||
-rw-r--r-- | mh/mh_init.c | 2 | ||||
-rw-r--r-- | mh/mh_list.c | 10 |
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 (); @@ -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 (); + } |