[metapost] Still experimenting with MPlib
luigi scarso
luigi.scarso at gmail.com
Wed Dec 14 18:12:48 CET 2016
On Tue, Dec 13, 2016 at 10:29 AM, Nicola <nvitacolonna at gmail.com> 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
More information about the metapost
mailing list