Re: Still experimenting with MPlib
luigi scarso <[email protected]>
| Newsgroups | gmane.comp.tex.metapost |
|---|---|
| Message-ID | <CAG5iGsBfx+LoU8Q=-+L8q6K-njSbUxgKrSxvJEceLjCdB9pziw@mail.gmail.com> |
On Tue, Dec 13, 2016 at 10:29 AM, Nicola <[email protected]> wrote: > Hi, > I'm still experimenting with MPlib, and there is something that I do not > understand. I hope you can help me. I have tried the following program: > > void imp_init_mp_instance() > { > MP_options *opt = mp_options(); > opt->ini_version = 0; > opt->mem_name = "mpost"; > opt->command_line = NULL; > opt->noninteractive = 1; > imp = mp_initialize(opt); > if (!imp) { exit(EXIT_FAILURE); } > mp_execute(imp, "\\", 1); // Flush MetaPost introductory text > } > > > int main(void) > { > imp_init_mp_instance(); > > char *line[] = { > "if 1=0:", > "message \"tsk\";", > "fi;" > }; > > for (int i = 0; i < 3; i++) { > mp_execute(imp, line[i], strlen(line[i])); > mp_run_data *cmd_result = mp_rundata(imp); > printf("%s", cmd_result->term_out.data); > printf("Status: %d\n", mp_status(imp)); > } > } > > This results in the MP instance being in an error state, and the error > message is the following: > > ! Missing `:' has been inserted. > <inserted text> > : > <to be read again> > fi > <*> fi > ; > ! A statement can't begin with `:'. > <to be read again> > : > <to be read again> > fi > <*> fi > ; > ! Extra tokens will be flushed. > <to be read again> > : > <to be read again> > fi > <*> fi > ; > > Of course, this doesn't work as in mpost's prompt, as I had naïvely > expected. So, should mp_execute() be fed with complete statements only? > > It is not clear to me what happens internally when mp_execute() is passed an > incomplete statement. For example, if I call: > > mp_execute(imp, "x=1", 3); // Note the missing semi-colon > mp_execute(imp, "show x;", 7); > > this acts as if x=1 was ignored (`show x` prints `>> x`). But the example > above seems to indicate that this is not the case (if incomplete statements > were ignored, I'd get an `! Extra fi.` error after the third invocation). > > > > Nicola > > -- > http://tug.org/metapost/ Basically, when mpost needs something, it reads the rest of the file or asks the user to put more material. In your program there is no file (only strings) and no user, and in this case the "if" needs to read tokens until the "else", "elseif" or "fi" , but it cannot do it because there is still nothing to read . In detail (see mp.w, rev. 2106 ): #0 mp_pass_text (mp=0x96f280) at ../../../source/texk/web2c/mplibdir/mp.w:20784 #1 0x0000000000458640 in mp_conditional (mp=0x96f280) at ../../../source/texk/web2c/mplibdir/mp.w:20931 #2 0x000000000045557f in mp_expand (mp=0x96f280) at ../../../source/texk/web2c/mplibdir/mp.w:19818 #3 0x00000000004568e1 in mp_get_x_next (mp=0x96f280) at ../../../source/texk/web2c/mplibdir/mp.w:20199 #4 0x0000000000477e22 in mp_do_statement (mp=0x96f280) at ../../../source/texk/web2c/mplibdir/mp.w:29301 #5 0x000000000047c78a in mp_execute (mp=0x96f280, s=0x70f713 "if 1=0:", l=7) at ../../../source/texk/web2c/mplibdir/mp.w:30695 #6 0x0000000000403792 in main () at main.c:49 @ Here is a procedure that ignores text until coming to an \&{elseif}, \&{else}, or \&{fi} at level zero of $\&{if}\ldots\&{fi}$ nesting. After it has acted, |cur_mod| will indicate the token that was found. \MP's smallest two command codes are |if_test| and |fi_or_else|; this makes the skipping process a bit simpler. @c void mp_pass_text (MP mp) { integer l = 0; mp->scanner_status = skipping; mp->warning_line = mp_true_line (mp); while (1) { get_t_next (mp); if (cur_cmd() <= mp_fi_or_else) { if (cur_cmd() < mp_fi_or_else) { incr (l); } else { if (l == 0) break; if (cur_mod() == fi_code) decr (l); } } else { @<Decrease the string reference count, if the current token is a string@>; } } mp->scanner_status = normal; } When we call get_t_next (mp); the buffer is p mp->buffer $2 = (ASCII_code *) 0x974aa0 "if 1=0:%a..." The '%' is added by mpost to mark the end of the line; mp_get_next asks a new chunk @c void mp_get_next (MP mp) { /* sets |cur_cmd|, |cur_mod|, |cur_sym| to next token */ mp_sym cur_sym_; /* speed up access */ RESTART: set_cur_sym(NULL); set_cur_sym_mod(0); if (file_state) { int k; /* an index into |buffer| */ ASCII_code c; /* the current character in the buffer */ int cclass; /* its class number */ /* Input from external file; |goto restart| if no input found, or |return| if a non-symbolic token is found */ /* A percent sign appears in |buffer[limit]|; this makes it unnecessary to have a special test for end-of-line. */ SWITCH: c = mp->buffer[loc]; incr (loc); cclass = mp->char_class[c]; switch (cclass) { case digit_class: scan_numeric_token((c - '0')); return; break; case period_class: cclass = mp->char_class[mp->buffer[loc]]; if (cclass > period_class) { goto SWITCH; } else if (cclass < period_class) { /* |class=digit_class| */ scan_fractional_token(0); return; } break; case space_class: goto SWITCH; break; case percent_class: if (mp->scanner_status == tex_flushing) { if (loc < limit) goto SWITCH; } /* Move to next line of file, or |goto restart| if there is no next line */ switch (move_to_next_line(mp)) { case 1: goto RESTART; break; case 2: goto COMMON_ENDING; break; default: break; } : Here the program fails in move_to_next_line(mp) because the name of the current file is NULL and we should stop at prompt_input 18955 if (mp->input_ptr > 0) { 18956 /* text was inserted during error recovery or by \&{scantokens} */ 18957 mp_end_file_reading (mp); 18958 /* goto RESTART */ 18959 return 1; /* resume previous level */ 18960 } 18961 if (mp->job_name == NULL 18962 && (mp->selector < log_only || mp->selector >= write_file)) 18963 mp_open_log_file (mp); 18964 if (mp->interaction > mp_nonstop_mode) { 18965 if (limit == start) /* previous line was empty */ 18966 mp_print_nl (mp, "(Please type a command or say `end')"); 18967 mp_print_ln (mp); 18968 mp->first = (size_t) start; 18969 prompt_input ("*"); But at prompt_input we execute the longjmp because mp->noninteractive 1778 void mp_term_input (MP mp) { /* gets a line from the terminal */ 1779 size_t k; /* index into |buffer| */ 1780 if (mp->noninteractive) { 1781 if (!mp_input_ln (mp, mp->term_in)) 1782 longjmp (*(mp->jump_buf), 1); /* chunk finished */ and we go back to mp_execute with return mp->history = 0 (which is not ok, btw) 30651 int mp_execute (MP mp, char *s, size_t l) { 30652 mp_reset_stream (&(mp->run_data.term_out)); 30653 mp_reset_stream (&(mp->run_data.log_out)); 30654 mp_reset_stream (&(mp->run_data.error_out)); 30655 mp_reset_stream (&(mp->run_data.ship_out)); 30656 if (mp->finished) { 30657 return mp->history; 30658 } else if (!mp->noninteractive) { 30659 mp->history = mp_fatal_error_stop; 30660 return mp->history; 30661 } 30662 if (mp->history < mp_fatal_error_stop) { 30663 xfree (mp->jump_buf); 30664 mp->jump_buf = malloc (sizeof (jmp_buf)); 30665 if (mp->jump_buf == NULL || setjmp (*(mp->jump_buf)) != 0) { 30666 return mp->history; 30667 } -- luigi -- http://tug.org/metapost/