diff options
Diffstat (limited to 'source/luametatex/source/lua/lmtmplib.c')
-rw-r--r-- | source/luametatex/source/lua/lmtmplib.c | 3137 |
1 files changed, 3137 insertions, 0 deletions
diff --git a/source/luametatex/source/lua/lmtmplib.c b/source/luametatex/source/lua/lmtmplib.c new file mode 100644 index 000000000..74d684c3f --- /dev/null +++ b/source/luametatex/source/lua/lmtmplib.c @@ -0,0 +1,3137 @@ +/* + See license.txt in the root of this project. +*/ + +/*tex + + This is an adapted version of \MPLIB\ for ConTeXt LMTX. Interfaces might change as experiments + demand or indicate this. It's meant for usage in \LUAMETATEX, which is an engine geared at + \CONTEXT, and which, although useable in production, will in principle be experimental and + moving for a while, depending on needs, experiments and mood. + + In \LUATEX\, at some point \MPLIB\ got an interface to \LUA\ and that greatly enhanced the + posibilities. In \LUAMETATEX\ this interface was further improved and in addition scanners + got added. So in the meantime we have a rather complete and tight integration of \TEX, \LUA, + and \METAPOST\ and in \CONTEXT\ we use that integration as much as possible. + + An important difference with \LUATEX\ is that we use an upgraded \MPLIB. The \POSTSCRIPT\ + backend has been dropped and there is no longer font and file related code because that + all goes via \LUA\ now. The binary frontend has been dropped to so we now only have scaled, + double and decimal. + +*/ + +# include "mpconfig.h" + +# include "mp.h" + +/*tex + We can use common definitions but can only load this file after we load the \METAPOST\ stuff, + which defines a whole bunch of macros that can clash. Using the next one is actually a good + check for such conflicts. +*/ + +# include "luametatex.h" + +/*tex + + Here are some enumeration arrays to map MPlib enums to \LUA\ strings. If needed we can also + predefine keys here, as we do with nodes. Some tables relate to the scanners. + +*/ + +# define MPLIB_PATH 0 +# define MPLIB_PEN 1 +# define MPLIB_PATH_SIZE 8 + +static const char *mplib_math_options[] = { + "scaled", + "double", + "binary", /* not available in luatex */ + "decimal", + NULL +}; + +static const char *mplib_interaction_options[] = { + "unknown", + "batch", + "nonstop", + "scroll", + "errorstop", + "silent", + NULL +}; + +static const char *mplib_filetype_names[] = { + "terminal", /* mp_filetype_terminal */ + "mp", /* mp_filetype_program */ + "data", /* mp_filetype_text */ + NULL +}; + +/* +static const char *knot_type_enum[] = { + "endpoint", "explicit", "given", "curl", "open", "end_cycle" +}; +*/ + +static const char *mplib_fill_fields[] = { + "type", + "path", "htap", + "pen", "color", + "linejoin", "miterlimit", + "prescript", "postscript", + "stacking", + NULL +}; + +static const char *mplib_stroked_fields[] = { + "type", + "path", + "pen", "color", + "linejoin", "miterlimit", "linecap", + "dash", + "prescript", "postscript", + "stacking", + NULL +}; + +static const char *mplib_start_clip_fields[] = { + "type", + "path", + "prescript", "postscript", + "stacking", + NULL +}; + +static const char *mplib_start_group_fields[] = { + "type", + "path", + "prescript", "postscript", + "stacking", + NULL +}; + +static const char *mplib_start_bounds_fields[] = { + "type", + "path", + "prescript", "postscript", + "stacking", + NULL +}; + +static const char *mplib_stop_clip_fields[] = { + "type", + "stacking", + NULL +}; + +static const char *mplib_stop_group_fields[] = { + "type", + "stacking", + NULL +}; + +static const char *mplib_stop_bounds_fields[] = { + "type", + "stacking", + NULL +}; + +static const char *mplib_no_fields[] = { + NULL +}; + +static const char *mplib_codes[] = { + "undefined", + "btex", /* mp_btex_command */ /* btex verbatimtex */ + "etex", /* mp_etex_command */ /* etex */ + "if", /* mp_if_test_command */ /* if */ + "fiorelse", /* mp_fi_or_else_command */ /* elseif else fi */ + "input", /* mp_input_command */ /* input endinput */ + "iteration", /* mp_iteration_command */ /* for forsuffixes forever endfor */ + "repeatloop", /* mp_repeat_loop_command */ /* used in repeat loop (endfor) */ + "exittest", /* mp_exit_test_command */ /* exitif */ + "relax", /* mp_relax_command */ /* \\ */ + "scantokens", /* mp_scan_tokens_command */ /* scantokens */ + "runscript", /* mp_runscript_command */ /* runscript */ + "maketext", /* mp_maketext_command */ /* maketext */ + "expandafter", /* mp_expand_after_command */ /* expandafter */ + "definedmacro", /* mp_defined_macro_command */ /* */ + "save", /* mp_save_command */ /* save */ + "interim", /* mp_interim_command */ /* interim */ + "let", /* mp_let_command */ /* let */ + "newinternal", /* mp_new_internal_command */ /* newinternal */ + "macrodef", /* mp_macro_def_command */ /* def vardef (etc) */ + "shipout", /* mp_ship_out_command */ /* shipout */ + "addto", /* mp_add_to_command */ /* addto */ + "setbounds", /* mp_bounds_command */ /* setbounds clip group */ + "protection", /* mp_protection_command */ + "property", /* mp_property_command */ + "show", /* mp_show_command */ /* show showvariable (etc) */ + "mode", /* mp_mode_command */ /* batchmode (etc) */ + "onlyset", /* mp_only_set_command */ /* randomseed, maxknotpool */ + "message", /* mp_message_command */ /* message errmessage */ + "everyjob", /* mp_every_job_command */ /* everyjob */ + "delimiters", /* mp_delimiters_command */ /* delimiters */ + "write", /* mp_write_command */ /* write */ + "typename", /* mp_type_name_command */ /* (declare) numeric pair */ + "leftdelimiter", /* mp_left_delimiter_command */ /* the left delimiter of a matching pair */ + "begingroup", /* mp_begin_group_command */ /* begingroup */ + "nullary", /* mp_nullary_command */ /* operator without arguments: normaldeviate (etc) */ + "unary", /* mp_unary_command */ /* operator with one argument: sqrt (etc) */ + "str", /* mp_str_command */ /* convert a suffix to a string: str */ + "void", /* mp_void_command */ /* convert a suffix to a boolean: void */ + "cycle", /* mp_cycle_command */ /* cycle */ + "ofbinary", /* mp_of_binary_command */ /* binary operation taking "of", like "point of" */ + "capsule", /* mp_capsule_command */ /* */ + "string", /* mp_string_command */ /* */ + "internal", /* mp_internal_quantity_command */ /* */ + "tag", /* mp_tag_command */ /* a symbolic token without a primitive meaning */ + "numeric", /* mp_numeric_command */ /* numeric constant */ + "plusorminus", /* mp_plus_or_minus_command */ /* + - */ + "secondarydef", /* mp_secondary_def_command */ /* secondarydef */ + "tertiarybinary", /* mp_tertiary_binary_command */ /* an operator at the tertiary level: ++ (etc) */ + "leftbrace", /* mp_left_brace_command */ /* { */ + "pathjoin", /* mp_path_join_command */ /* .. */ + "ampersand", /* mp_ampersand_command */ /* & */ + "tertiarydef", /* mp_tertiary_def_command */ /* tertiarydef */ + "primarybinary", /* mp_primary_binary_command */ /* < (etc) */ + "equals", /* mp_equals_command */ /* = */ + "and", /* mp_and_command */ /* and */ + "primarydef", /* mp_primary_def_command */ /* primarydef */ + "slash", /* mp_slash_command */ /* / */ + "secondarybinary", /* mp_secondary_binary_command */ /* an operator at the binary level: shifted (etc) */ + "parametertype", /* mp_parameter_commmand */ /* primary expr suffix (etc) */ + "controls", /* mp_controls_command */ /* controls */ + "tension", /* mp_tension_command */ /* tension */ + "atleast", /* mp_at_least_command */ /* atleast */ + "curl", /* mp_curl_command */ /* curl */ + "macrospecial", /* mp_macro_special_command */ /* quote, #@ (etc) */ + "rightdelimiter", /* mp_right_delimiter_command */ /* the right delimiter of a matching pair */ + "leftbracket", /* mp_left_bracket_command */ /* [ */ + "rightbracket", /* mp_right_bracket_command */ /* ] */ + "rightbrace", /* mp_right_brace_command */ /* } */ + "with", /* mp_with_option_command */ /* withpen (etc) */ + "thingstoadd", /* mp_thing_to_add_command */ /* addto contour doublepath also */ + "of", /* mp_of_command */ /* of */ + "to", /* mp_to_command */ /* to */ + "step", /* mp_step_command */ /* step */ + "until", /* mp_until_command */ /* until */ + "within", /* mp_within_command */ /* within */ + "assignment", /* mp_assignment_command */ /* := */ + "colon", /* mp_colon_command */ /* : */ + "comma", /* mp_comma_command */ /* , */ + "semicolon", /* mp_semicolon_command */ /* ; */ + "endgroup", /* mp_end_group_command */ /* endgroup */ + "stop", /* mp_stop_command */ /* end dump */ + // "outertag", /* mp_outer_tag_command */ /* protection code added to command code */ + "undefinedcs", /* mp_undefined_cs_command */ /* protection code added to command code */ + NULL +}; + +static const char *mplib_states[] = { + "normal", + "skipping", + "flushing", + "absorbing", + "var_defining", + "op_defining", + "loop_defining", + NULL +}; + +static const char *mplib_types[] = { + "undefined", /* mp_undefined_type */ + "vacuous", /* mp_vacuous_type */ + "boolean", /* mp_boolean_type */ + "unknownboolean", /* mp_unknown_boolean_type */ + "string", /* mp_string_type */ + "unknownstring", /* mp_unknown_string_type */ + "pen", /* mp_pen_type */ + "unknownpen", /* mp_unknown_pen_type */ + "nep", /* mp_nep_type */ + "unknownnep", /* mp_unknown_nep_type */ + "path", /* mp_path_type */ + "unknownpath", /* mp_unknown_path_type */ + "picture", /* mp_picture_type */ + "unknownpicture", /* mp_unknown_picture_type */ + "transform", /* mp_transform_type */ + "color", /* mp_color_type */ + "cmykcolor", /* mp_cmykcolor_type */ + "pair", /* mp_pair_type */ + // "script", /* */ + "numeric", /* mp_numeric_type */ + "known", /* mp_known_type */ + "dependent", /* mp_dependent_type */ + "protodependent", /* mp_proto_dependent_type */ + "independent", /* mp_independent_type */ + "tokenlist", /* mp_token_list_type */ + "structured", /* mp_structured_type */ + "unsuffixedmacro", /* mp_unsuffixed_macro_type */ + "suffixedmacro", /* mp_suffixed_macro_type */ + NULL +}; + +static const char *mplib_colormodels[] = { + "no", + "grey", + "rgb", + "cmyk", + NULL +}; + +/*tex Some statistics. */ + +typedef struct mplib_state_info { + int file_callbacks; + int text_callbacks; + int script_callbacks; + int log_callbacks; + int overload_callbacks; + int error_callbacks; + int warning_callbacks; +} mplib_state_info; + +static mplib_state_info mplib_state = { + .file_callbacks = 0, + .text_callbacks = 0, + .script_callbacks = 0, + .log_callbacks = 0, + .overload_callbacks = 0, + .error_callbacks = 0, + .warning_callbacks = 0, +}; + +/*tex + + We need a few metatable identifiers in order to access the metatables for the main object and result + userdata. The following code is now replaced by the method that uses keys. + +*/ + +/* +# define MP_METATABLE_INSTANCE "mp.instance" +# define MP_METATABLE_FIGURE "mp.figure" +# define MP_METATABLE_OBJECT "mp.object" +*/ + +/*tex + This is used for heuristics wrt curves or lines. The default value is rather small + and often leads to curved rectangles. + + \starttabulate[||||] + \NC 131/65536.0 \NC 0.0019989013671875 \NC default \NC \NR + \NC 0.001 * 0x7FFF/0x4000 \NC 0.0019999389648438 \NC kind of the default \NC \NR + \NC 32/16000.0 \NC 0.002 \NC somewhat cleaner \NC \NR + \NC 10/ 2000.0 \NC 0.005 \NC often good enough \NC \NR + \stoptabulate + +*/ + +# define default_bend_tolerance 131/65536.0 +# define default_move_tolerance 131/65536.0 + +typedef enum mp_variables { + mp_bend_tolerance = 1, + mp_move_tolerance = 2, +} mp_variables; + +static lua_Number mplib_aux_get_bend_tolerance(lua_State *L, int slot) +{ + lua_Number tolerance; + lua_getiuservalue(L, slot, mp_bend_tolerance); + tolerance = lua_tonumber(L, -1); + lua_pop(L, 1); + return tolerance; +} + +static lua_Number mplib_aux_get_move_tolerance(lua_State *L, int slot) +{ + lua_Number tolerance; + lua_getiuservalue(L, slot, mp_move_tolerance); + tolerance = lua_tonumber(L, -1); + lua_pop(L, 1); + return tolerance; +} + +static void mplib_aux_set_bend_tolerance(lua_State *L, lua_Number tolerance) +{ + lua_pushnumber(L, tolerance); + lua_setiuservalue(L, -2, mp_bend_tolerance); +} + +static void mplib_aux_set_move_tolerance(lua_State *L, lua_Number tolerance) +{ + lua_pushnumber(L, tolerance); + lua_setiuservalue(L, -2, mp_move_tolerance); +} + +inline static char *lmt_string_from_index(lua_State *L, int n) +{ + size_t l; + const char *x = lua_tolstring(L, n, &l); + // return (x && l > 0) ? lmt_generic_strdup(x) : NULL; + return (x && l > 0) ? lmt_memory_strdup(x) : NULL; +} + +inline static char *lmt_lstring_from_index(lua_State *L, int n, size_t *l) +{ + const char *x = lua_tolstring(L, n, l); + // return (x && l > 0) ? lmt_generic_strdup(x) : NULL; + return (x && l > 0) ? lmt_memory_strdup(x) : NULL; +} + +static void mplib_aux_invalid_object_warning(const char * detail) +{ + tex_formatted_warning("mp lib","lua <mp %s> expected", detail); +} + +static void mplib_aux_invalid_object_error(const char * detail) +{ + tex_formatted_error("mp lib","lua <mp %s> expected", detail); +} + +inline static MP *mplib_aux_is_mpud(lua_State *L, int n) +{ + MP *p = (MP *) lua_touserdata(L, n); + if (p && lua_getmetatable(L, n)) { + lua_get_metatablelua(mplib_instance); + if (! lua_rawequal(L, -1, -2)) { + p = NULL; + } + lua_pop(L, 2); + if (p) { + return p; + } + } + mplib_aux_invalid_object_error("instance"); + return NULL; +} + +inline static MP mplib_aux_is_mp(lua_State *L, int n) +{ + MP *p = (MP *) lua_touserdata(L, n); + if (p && lua_getmetatable(L, n)) { + lua_get_metatablelua(mplib_instance); + if (! lua_rawequal(L, -1, -2)) { + p = NULL; + } + lua_pop(L, 2); + if (p) { + return *p; + } + } + mplib_aux_invalid_object_error("instance"); + return NULL; +} + +inline static mp_edge_object **mplib_aux_is_figure(lua_State *L, int n) +{ + mp_edge_object **p = (mp_edge_object **) lua_touserdata(L, n); + if (p && lua_getmetatable(L, n)) { + lua_get_metatablelua(mplib_figure); + if (! lua_rawequal(L, -1, -2)) { + p = NULL; + } + lua_pop(L, 2); + if (p) { + return p; + } + } + mplib_aux_invalid_object_warning("figure"); + return NULL; +} + +inline static mp_graphic_object **mplib_aux_is_gr_object(lua_State *L, int n) +{ + mp_graphic_object **p = (mp_graphic_object **) lua_touserdata(L, n); + if (p && lua_getmetatable(L, n)) { + lua_get_metatablelua(mplib_object); + if (! lua_rawequal(L, -1, -2)) { + p = NULL; + } + lua_pop(L, 2); + if (p) { + return p; + } + } + mplib_aux_invalid_object_warning("object"); + return NULL; +} + +/*tex In the next array entry 0 is not used */ + +static int mplib_values_type[mp_stop_bounds_code + 1] = { 0 }; +static int mplib_values_knot[6] = { 0 }; + +static void mplib_aux_initialize_lua(lua_State *L) +{ + (void) L; + mplib_values_type[mp_fill_code] = lua_key_index(fill); + mplib_values_type[mp_stroked_code] = lua_key_index(outline); + mplib_values_type[mp_start_clip_code] = lua_key_index(start_clip); + mplib_values_type[mp_start_group_code] = lua_key_index(start_group); + mplib_values_type[mp_start_bounds_code] = lua_key_index(start_bounds); + mplib_values_type[mp_stop_clip_code] = lua_key_index(stop_clip); + mplib_values_type[mp_stop_group_code] = lua_key_index(stop_group); + mplib_values_type[mp_stop_bounds_code] = lua_key_index(stop_bounds); + + mplib_values_knot[mp_endpoint_knot] = lua_key_index(endpoint); + mplib_values_knot[mp_explicit_knot] = lua_key_index(explicit); + mplib_values_knot[mp_given_knot] = lua_key_index(given); + mplib_values_knot[mp_curl_knot] = lua_key_index(curl); + mplib_values_knot[mp_open_knot] = lua_key_index(open); + mplib_values_knot[mp_end_cycle_knot] = lua_key_index(end_cycle); +} + +static void mplib_aux_push_pentype(lua_State *L, mp_gr_knot h) +{ + if (h && h == h->next) { + lua_push_value_at_key(L, type, elliptical); + } +} + +static int mplib_set_tolerance(lua_State *L) +{ + MP mp = mplib_aux_is_mp(L, 1); + if (mp) { + mplib_aux_set_bend_tolerance(L, luaL_optnumber(L, 2, default_bend_tolerance)); + mplib_aux_set_move_tolerance(L, luaL_optnumber(L, 3, default_move_tolerance)); + } + return 0; +} + +static int mplib_get_tolerance(lua_State *L) +{ + + if (lua_type(L, 1) == LUA_TUSERDATA) { + MP mp = mplib_aux_is_mp(L, 1); + if (mp) { + lua_pushnumber(L, mplib_aux_get_bend_tolerance(L, 1)); + lua_pushnumber(L, mplib_aux_get_move_tolerance(L, 1)); + return 2; + } else { + return 0; + } + } else { + lua_pushnumber(L, default_bend_tolerance); + lua_pushnumber(L, default_move_tolerance); + return 2; + } +} + +/*tex + + We start by defining the needed callback routines for the library. We could store them per + instance but it has no advantages so that will be done when we feel the need. + +*/ + +static int mplib_aux_register_function(lua_State *L, int old_id) +{ + if (! (lua_isfunction(L, -1) || lua_isnil(L, -1))) { + return 0; + } else { + lua_pushvalue(L, -1); + if (old_id) { + luaL_unref(L, LUA_REGISTRYINDEX, old_id); + } + return luaL_ref(L, LUA_REGISTRYINDEX); /*tex |new_id| */ + } +} + +static int mplib_aux_find_file_function(lua_State *L, MP_options *options) +{ + options->find_file_id = mplib_aux_register_function(L, options->find_file_id); + return (! options->find_file_id); +} + +static int mplib_aux_run_script_function(lua_State *L, MP_options *options) +{ + options->run_script_id = mplib_aux_register_function(L, options->run_script_id); + return (! options->run_script_id); +} + +static int mplib_aux_run_internal_function(lua_State *L, MP_options *options) +{ + options->run_internal_id = mplib_aux_register_function(L, options->run_internal_id); + return (! options->run_internal_id); +} + +static int mplib_aux_make_text_function(lua_State *L, MP_options *options) +{ + options->make_text_id = mplib_aux_register_function(L, options->make_text_id); + return (! options->make_text_id); +} + +static int mplib_aux_run_logger_function(lua_State *L, MP_options *options) +{ + options->run_logger_id = mplib_aux_register_function(L, options->run_logger_id); + return (! options->run_logger_id); +} + +static int mplib_aux_run_overload_function(lua_State *L, MP_options *options) +{ + options->run_overload_id = mplib_aux_register_function(L, options->run_overload_id); + return (! options->run_overload_id); +} + +static int mplib_aux_run_error_function(lua_State *L, MP_options *options) +{ + options->run_error_id = mplib_aux_register_function(L, options->run_error_id); + return (! options->run_error_id); +} + +static int mplib_aux_open_file_function(lua_State *L, MP_options *options) +{ + options->open_file_id = mplib_aux_register_function(L, options->open_file_id); + return (! options->open_file_id); +} + +static char *mplib_aux_find_file(MP mp, const char *fname, const char *fmode, int ftype) +{ + if (mp->find_file_id) { + lua_State *L = (lua_State *) mp_userdata(mp); + int stacktop = lua_gettop(L); + char *s = NULL; + lua_rawgeti(L, LUA_REGISTRYINDEX, mp->find_file_id); + lua_pushstring(L, fname); + lua_pushstring(L, fmode); + if (ftype > mp_filetype_text) { + lua_pushinteger(L, (lua_Integer) ftype - mp_filetype_text); + } else { + lua_pushstring(L, mplib_filetype_names[ftype]); + } + ++mplib_state.file_callbacks; + if (lua_pcall(L, 3, 1, 0)) { + tex_formatted_warning("mplib", "find file: %s", lua_tostring(L, -1)); + } else { + s = lmt_string_from_index(L, -1); + } + lua_settop(L, stacktop); + return s; + } else if (fmode[0] != 'r' || (! access(fname, R_OK)) || ftype) { + // return lmt_generic_strdup(fname); + return lmt_memory_strdup(fname); + } + return NULL; +} + +/* + + In retrospect we could have has a more granular approach: a flag that tells that the result is + a string to be scanned. Maybe I'll that anyway but it definitely means an adaption of the low + level interface we have in MetaFun. Also, the interface would be a bit ugly because then we + would like to handle values as in the 'whatever' function which in turn means a flag indicating + that a string is a string and not something to be scanned as tokens, and being compatible then + means that this flag comes after the string which kind of conflicts with multiple (number) + arguments ... in the end we gain too little to accept such an ugly mess as option. So, we stick + to: + + -- nil : nothing is injected and no scanning will happen + -- string : passed on in order to be scanned + -- table : passed on concatenated in order to be scanned + -- number : injected as numeric (so no scanning later on) + -- boolean : injected as boolean (so no scanning later on) + + and dealing with other datatypes is delegated to the injectors. + +*/ + +static int mplib_aux_with_path(lua_State *L, MP mp, int index, int what, int multiple); + +static void mplib_aux_inject_whatever(lua_State *L, MP mp, int index) +{ + switch (lua_type(L, index)) { + case LUA_TBOOLEAN: + mp_push_boolean_value(mp, lua_toboolean(L, index)); + break; + case LUA_TNUMBER: + mp_push_numeric_value(mp, lua_tonumber(L, index)); + break; + case LUA_TSTRING: + { + size_t l; + const char *s = lua_tolstring(L, index, &l); + mp_push_string_value(mp, s, (int) l); + break; + } + case LUA_TTABLE: + { + if (lua_rawgeti(L, index, 1) == LUA_TTABLE) { + /* table of tables */ + lua_pop(L, 1); + mplib_aux_with_path(L, mp, index, 1, 0); + } else { + lua_pop(L, 1); + switch (lua_rawlen(L, index)) { + case 2 : + mp_push_pair_value(mp, + lmt_number_from_table(L, index, 1, 0.0), + lmt_number_from_table(L, index, 2, 0.0) + ); + break; + case 3 : + mp_push_color_value(mp, + lmt_number_from_table(L, index, 1, 0.0), + lmt_number_from_table(L, index, 2, 0.0), + lmt_number_from_table(L, index, 3, 0.0) + ); + break; + case 4 : + mp_push_cmykcolor_value(mp, + lmt_number_from_table(L, index, 1, 0.0), + lmt_number_from_table(L, index, 2, 0.0), + lmt_number_from_table(L, index, 3, 0.0), + lmt_number_from_table(L, index, 4, 0.0) + ); + break; + case 6 : + mp_push_transform_value(mp, + lmt_number_from_table(L, index, 1, 0.0), + lmt_number_from_table(L, index, 2, 0.0), + lmt_number_from_table(L, index, 3, 0.0), + lmt_number_from_table(L, index, 4, 0.0), + lmt_number_from_table(L, index, 5, 0.0), + lmt_number_from_table(L, index, 6, 0.0) + ); + break; + } + } + break; + } + } +} + +static char *mplib_aux_return_whatever(lua_State *L, MP mp, int index) +{ + switch (lua_type(L, index)) { + case LUA_TBOOLEAN: + mp_push_boolean_value(mp, lua_toboolean(L, index)); + break; + case LUA_TNUMBER: + mp_push_numeric_value(mp, lua_tonumber(L, index)); + break; + /* A string is passed to scantokens. */ + case LUA_TSTRING: + return lmt_string_from_index(L, index); + /*tex A table is concatenated and passed to scantokens. */ + case LUA_TTABLE: + { + luaL_Buffer b; + lua_Integer n = (lua_Integer) lua_rawlen(L, index); + luaL_buffinit(L, &b); + for (lua_Integer i = 1; i <= n; i++) { + lua_rawgeti(L, index, i); + luaL_addvalue(&b); + lua_pop(L, 1); + } + luaL_pushresult(&b); + return lmt_string_from_index(L, -1); + } + } + return NULL; +} + +static char *mplib_aux_run_script(MP mp, const char *str, size_t len, int n) +{ + if (mp->run_script_id) { + lua_State *L = (lua_State *) mp_userdata(mp); + int stacktop = lua_gettop(L); + lua_rawgeti(L, LUA_REGISTRYINDEX, mp->run_script_id); + if (str) { + lua_pushlstring(L, str, len); + } else if (n > 0) { + lua_pushinteger(L, n); + } else { + lua_pushnil(L); + } + ++mplib_state.script_callbacks; + if (lua_pcall(L, 1, 2, 0)) { + tex_formatted_warning("mplib", "run script: %s", lua_tostring(L, -1)); + } else if (lua_toboolean(L, -1)) { + /* value boolean */ + mplib_aux_inject_whatever(L, mp, -2); + lua_settop(L, stacktop); + return NULL; + } else { + /* value nil */ + char *s = mplib_aux_return_whatever(L, mp, -2); + lua_settop(L, stacktop); + return s; + } + } + return NULL; +} + +void mplib_aux_run_internal(MP mp, int action, int n, int type, const char *name) +{ + if (mp->run_internal_id) { + lua_State *L = (lua_State *) mp_userdata(mp); + ++mplib_state.script_callbacks; /* maybe a special counter */ + lua_rawgeti(L, LUA_REGISTRYINDEX, mp->run_internal_id); + lua_pushinteger(L, action); /* 0=initialize, 1=save, 2=restore */ + lua_pushinteger(L, n); + if (name) { + lua_pushinteger(L, type); + lua_pushstring(L, name); + if (! lua_pcall(L, 4, 0, 0)) { + return; + } + } else { + if (lua_pcall(L, 2, 0, 0)) { + return; + } + } + tex_formatted_warning("mplib", "run internal: %s", lua_tostring(L, -1)); + } +} + +static char *mplib_aux_make_text(MP mp, const char *str, size_t len, int mode) +{ + if (mp->make_text_id) { + lua_State *L = (lua_State *) mp_userdata(mp); + int stacktop = lua_gettop(L); + char *s = NULL; + lua_rawgeti(L, LUA_REGISTRYINDEX, mp->make_text_id); + lua_pushlstring(L, str, len); + lua_pushinteger(L, mode); + ++mplib_state.text_callbacks; + if (lua_pcall(L, 2, 1, 0)) { + tex_formatted_warning("mplib", "make text: %s", lua_tostring(L, -1)); + } else { + s = lmt_string_from_index(L, -1); + } + lua_settop(L, stacktop); + return s; + } + return NULL; +} + +static void mplib_aux_run_logger(MP mp, int target, const char *str, size_t len) +{ + if (mp->run_logger_id) { + lua_State *L = (lua_State *) mp_userdata(mp); + int stacktop = lua_gettop(L); + lua_rawgeti(L, LUA_REGISTRYINDEX, mp->run_logger_id); + lua_pushinteger(L, target); + lua_pushlstring(L, str, len); + ++mplib_state.log_callbacks; + if (lua_pcall(L, 2, 0, 0)) { + tex_formatted_warning("mplib", "run logger: %s", lua_tostring(L, -1)); + } + lua_settop(L, stacktop); + } +} + +static int mplib_aux_run_overload(MP mp, int property, const char *str, int mode) +{ + int quit = 0; + if (mp->run_overload_id) { + lua_State *L = (lua_State *) mp_userdata(mp); + int stacktop = lua_gettop(L); + lua_rawgeti(L, LUA_REGISTRYINDEX, mp->run_overload_id); + lua_pushinteger(L, property); + lua_pushstring(L, str); + lua_pushinteger(L, mode); + ++mplib_state.overload_callbacks; + if (lua_pcall(L, 3, 1, 0)) { + tex_formatted_warning("mplib", "run overload: %s", lua_tostring(L, -1)); + quit = 1; + } else { + quit = lua_toboolean(L, -1); + } + lua_settop(L, stacktop); + } + return quit; +} + +static void mplib_aux_run_error(MP mp, const char *str, const char *help, int interaction) +{ + if (mp->run_error_id) { + lua_State *L = (lua_State *) mp_userdata(mp); + int stacktop = lua_gettop(L); + lua_rawgeti(L, LUA_REGISTRYINDEX, mp->run_error_id); + lua_pushstring(L, str); + lua_pushstring(L, help); + lua_pushinteger(L, interaction); + ++mplib_state.error_callbacks; + if (lua_pcall(L, 3, 0, 0)) { + tex_formatted_warning("mplib", "run error: %s", lua_tostring(L, -1)); + } + lua_settop(L, stacktop); + } +} + +/* + + We keep all management in Lua, so for now we don't create an object. Files are normally closed + anyway. We can always make it nicer. The opener has to return an integer. A value zero indicates + that no file is opened. + +*/ + +/* index needs checking, no need for pointer (was old) */ + +static void *mplib_aux_open_file(MP mp, const char *fname, const char *fmode, int ftype) +{ + if (mp->open_file_id) { + int *index = mp_memory_allocate(sizeof(int)); + if (index) { + lua_State *L = (lua_State *) mp_userdata(mp); + int stacktop = lua_gettop(L); + lua_rawgeti(L, LUA_REGISTRYINDEX, mp->open_file_id); + lua_pushstring(L, fname); + lua_pushstring(L, fmode); + if (ftype > mp_filetype_text) { + lua_pushinteger(L, (lua_Integer) ftype - mp_filetype_text); + } else { + lua_pushstring(L, mplib_filetype_names[ftype]); + } + ++mplib_state.file_callbacks; + if (lua_pcall(L, 3, 1, 0)) { + *((int*) index) = 0; + } else if (lua_istable(L, -1)) { + lua_pushvalue(L, -1); + *((int*) index) = luaL_ref(L, LUA_REGISTRYINDEX); + } else { + tex_normal_warning("mplib", "open file: table expected"); + *((int*) index) = 0; + } + lua_settop(L, stacktop); + return index; + } + } + return NULL; +} + +# define mplib_pop_function(idx,tag) \ + lua_rawgeti(L, LUA_REGISTRYINDEX, idx); \ + lua_push_key(tag); \ + lua_rawget(L, -2); + +static void mplib_aux_close_file(MP mp, void *index) +{ + if (mp->open_file_id && index) { + int idx = *((int*) index); + lua_State *L = (lua_State *) mp_userdata(mp); + int stacktop = lua_gettop(L); + mplib_pop_function(idx, close) + if (lua_isfunction(L, -1)) { + ++mplib_state.file_callbacks; + if (lua_pcall(L, 0, 0, 0)) { + /* no message */ + } else { + /* nothing to be done here */ + } + } + /* + if (index) { + luaL_unref(L, idx)); + } + */ + lua_settop(L, stacktop); + mp_memory_free(index); + } +} + +static char *mplib_aux_read_file(MP mp, void *index, size_t *size) +{ + if (mp->open_file_id && index) { + lua_State *L = (lua_State *) mp_userdata(mp); + int stacktop = lua_gettop(L); + int idx = *((int*) index); + char *s = NULL; + mplib_pop_function(idx, reader) + if (lua_isfunction(L, -1)) { + ++mplib_state.file_callbacks; + if (lua_pcall(L, 0, 1, 0)) { + *size = 0; + } else if (lua_type(L, -1) == LUA_TSTRING) { + s = lmt_lstring_from_index(L, -1, size); + } + } + lua_settop(L, stacktop); + return s; + } + return NULL; +} + +static void mplib_aux_write_file(MP mp, void *index, const char *s) +{ + if (mp->open_file_id && index) { + lua_State *L = (lua_State *) mp_userdata(mp); + int stacktop = lua_gettop(L); + int idx = *((int*) index); + mplib_pop_function(idx, writer) + if (lua_isfunction(L, -1)) { + lua_pushstring(L, s); + ++mplib_state.file_callbacks; + if (lua_pcall(L, 1, 0, 0)) { + /* no message */ + } else { + /* nothing to be done here */ + } + } + lua_settop(L, stacktop); + } +} + +static int mplib_scan_next(lua_State *L) +{ + MP mp = mplib_aux_is_mp(L, 1); + int token = 0; + int mode = 0; + int kind = 0; + if (mp) { + int keep = 0 ; + if (lua_gettop(L) > 1) { + keep = lua_toboolean(L, 2); + } + mp_scan_next_value(mp, keep, &token, &mode, &kind); + } + lua_pushinteger(L, token); + lua_pushinteger(L, mode); + lua_pushinteger(L, kind); + return 3; +} + +static int mplib_scan_expression(lua_State *L) +{ + MP mp = mplib_aux_is_mp(L, 1); + int kind = 0; + if (mp) { + int keep = 0 ; + if (lua_gettop(L) > 1) { + keep = lua_toboolean(L, 2); + } + mp_scan_expr_value(mp, keep, &kind); + } + lua_pushinteger(L, kind); + return 1; +} + +static int mplib_scan_token(lua_State *L) +{ + MP mp = mplib_aux_is_mp(L, 1); + int token = 0; + int mode = 0; + int kind = 0; + if (mp) { + int keep = 0 ; + if (lua_gettop(L) > 1) { + keep = lua_toboolean(L, 2); + } + mp_scan_token_value(mp, keep, &token, &mode, &kind); + } + lua_pushinteger(L, token); + lua_pushinteger(L, mode); + lua_pushinteger(L, kind); + return 3; +} + +static int mplib_skip_token(lua_State *L) +{ + MP mp = mplib_aux_is_mp(L, 1); + lua_pushboolean(L, mp ? mp_skip_token_value(mp, lmt_tointeger(L, 2)) : 0); + return 1; +} + +static int mplib_scan_symbol(lua_State *L) +{ + MP mp = mplib_aux_is_mp(L, 1); + if (mp) { + int keep = 0 ; + int expand = 1 ; + int top = lua_gettop(L) ; + char *s = NULL; + if (top > 2) { // no need to check + expand = lua_toboolean(L, 3); + } + if (top > 1) { // no need to check + keep = lua_toboolean(L, 2); + } + mp_scan_symbol_value(mp, keep, &s, expand) ; + if (s) { + /* we could do without the copy */ + lua_pushstring(L, s); + mp_memory_free(s); + return 1; + } + } + lua_pushliteral(L,""); + return 1; +} + +static int mplib_scan_property(lua_State *L) +{ + MP mp = mplib_aux_is_mp(L, 1); + if (mp) { + int keep = 0 ; + int top = lua_gettop(L) ; + int type = 0 ; + int property = 0 ; + int detail = 0 ; + char *s = NULL; + if (top > 1) { + keep = lua_toboolean(L, 2); + } + mp_scan_property_value(mp, keep, &type, &s, &property, &detail); + if (s) { + lua_pushinteger(L, type); + lua_pushstring(L, s); + lua_pushinteger(L, property); + lua_pushinteger(L, detail); + mp_memory_free(s); + return 4; + } + } + return 0; +} + +/*tex + A circle has 8 points and a square 4 so let's just start with 8 slots in the table. +*/ + +static int aux_is_curved_gr(mp_gr_knot ith, mp_gr_knot pth, lua_Number tolerance) +{ + lua_Number d = pth->left_x - ith->right_x; + if (fabs(ith->right_x - ith->x_coord - d) <= tolerance && fabs(pth->x_coord - pth->left_x - d) <= tolerance) { + d = pth->left_y - ith->right_y; + if (fabs(ith->right_y - ith->y_coord - d) <= tolerance && fabs(pth->y_coord - pth->left_y - d) <= tolerance) { + return 0; + } + } + return 1; +} + +static int aux_is_duplicate_gr(mp_gr_knot pth, mp_gr_knot nxt, lua_Number tolerance) +{ + return (fabs(pth->x_coord - nxt->x_coord) <= tolerance && fabs(pth->y_coord - nxt->y_coord) <= tolerance); +} + +# define valid_knot_type(t) (t >= mp_endpoint_knot && t <= mp_end_cycle_knot) /* pens can act weird here */ + +// -68.031485 2.83464 l +// -68.031485 2.83464 -68.031485 -2.83464 -68.031485 -2.83464 c + +static void mplib_aux_push_path(lua_State *L, mp_gr_knot h, int ispen, lua_Number bendtolerance, lua_Number movetolerance) +{ + if (h) { + int i = 0; + mp_gr_knot p = h; + mp_gr_knot q = h; + int iscycle = 1; + lua_createtable(L, ispen ? 1 : MPLIB_PATH_SIZE, ispen ? 2 : 1); + do { + mp_gr_knot n = p->next; + int lt = p->left_type; + int rt = p->right_type; + if (ispen) { + lua_createtable(L, 0, 6); + // } else if (i > 0 && p != h && aux_is_duplicate_gr(p, n, movetolerance) && ! aux_is_curved_gr(p, n, bendtolerance) ) { + } else if (i > 0 && p != h && n != h && aux_is_duplicate_gr(p, n, movetolerance) && ! aux_is_curved_gr(p, n, bendtolerance) ) { + n->left_x = p->left_x; + n->left_y = p->left_y; + goto NEXTONE; + } else { + int ln = lt != mp_explicit_knot; + int rn = rt != mp_explicit_knot; + int ic = i > 0 && aux_is_curved_gr(q, p, bendtolerance); + int st = p->state; + lua_createtable(L, 0, 6 + (ic ? 1 : 0) + (ln ? 1 : 0) + (rn ? 1 : 0) + (st ? 1: 0)); + if (ln && valid_knot_type(lt)) { + lua_push_svalue_at_key(L, left_type, mplib_values_knot[lt]); + } + if (rn && valid_knot_type(rt)) { + lua_push_svalue_at_key(L, right_type, mplib_values_knot[rt]); + } + if (ic) { + lua_push_boolean_at_key(L, curved, 1); + } + if (st) { + lua_push_integer_at_key(L, state, st); + } + lua_push_number_at_key(L, x_coord, p->x_coord); + lua_push_number_at_key(L, y_coord, p->y_coord); + lua_push_number_at_key(L, left_x, p->left_x); + lua_push_number_at_key(L, left_y, p->left_y); + lua_push_number_at_key(L, right_x, p->right_x); + lua_push_number_at_key(L, right_y, p->right_y); + lua_rawseti(L, -2, ++i); + if (rt == mp_endpoint_knot) { + iscycle = 0; + break; + } + } + NEXTONE: + q = p; + p = n; + } while (p && p != h); + if (iscycle && i > 1 && aux_is_curved_gr(q, h, bendtolerance)) { + lua_rawgeti(L, -1, 1); + lua_push_boolean_at_key(L, curved, 1); + lua_pop(L, 1); + } + if (ispen) { + lua_push_boolean_at_key(L, pen, 1); + } + lua_push_boolean_at_key(L, cycle, iscycle); + } else { + lua_pushnil(L); + } +} + +static int aux_is_curved(MP mp, mp_knot ith, mp_knot pth, lua_Number tolerance) +{ + lua_Number d = mp_number_as_double(mp, pth->left_x) - mp_number_as_double(mp, ith->right_x); + if (fabs(mp_number_as_double(mp, ith->right_x) - mp_number_as_double(mp, ith->x_coord) - d) <= tolerance && fabs(mp_number_as_double(mp, pth->x_coord) - mp_number_as_double(mp, pth->left_x) - d) <= tolerance) { + d = mp_number_as_double(mp, pth->left_y) - mp_number_as_double(mp, ith->right_y); + if (fabs(mp_number_as_double(mp, ith->right_y) - mp_number_as_double(mp, ith->y_coord) - d) <= tolerance && fabs(mp_number_as_double(mp, pth->y_coord) - mp_number_as_double(mp, pth->left_y) - d) <= tolerance) { + return 0; + } + } + return 1; +} + +static void aux_mplib_knot_to_path(lua_State *L, MP mp, mp_knot h, int ispen, int compact, int check, lua_Number bendtolerance) // maybe also movetolerance +{ + int i = 1; + mp_knot p = h; + mp_knot q = h; + int iscycle = 1; + lua_createtable(L, ispen ? 1 : MPLIB_PATH_SIZE, ispen ? 2 : 1); + if (compact) { + do { + lua_createtable(L, 2, 0); + lua_push_number_at_index(L, 1, mp_number_as_double(mp, p->x_coord)); + lua_push_number_at_index(L, 2, mp_number_as_double(mp, p->y_coord)); + lua_rawseti(L, -2, i++); + if (p->right_type == mp_endpoint_knot) { + iscycle = 0; + break; + } else { + p = p->next; + } + } while (p && p != h); + } else { + do { + int lt = p->left_type; + int rt = p->right_type; + int ln = lt != mp_explicit_knot; + int rn = rt != mp_explicit_knot; + int ic = check && (i > 1) && aux_is_curved(mp, q, p, bendtolerance); + lua_createtable(L, 0, 6 + (ic ? 1 : 0) + (ln ? 1 : 0) + (rn ? 1 : 0)); + if (ln && valid_knot_type(lt)) { + lua_push_svalue_at_key(L, left_type, mplib_values_knot[lt]); + } else { + /* a pen */ + } + if (rn && valid_knot_type(rt)) { + lua_push_svalue_at_key(L, right_type, mplib_values_knot[rt]); + } else { + /* a pen */ + } + if (ic) { + lua_push_boolean_at_key(L, curved, 1); + } + lua_push_number_at_index(L, 1, mp_number_as_double(mp, p->x_coord)); + lua_push_number_at_index(L, 2, mp_number_as_double(mp, p->y_coord)); + lua_push_number_at_index(L, 3, mp_number_as_double(mp, p->left_x)); + lua_push_number_at_index(L, 4, mp_number_as_double(mp, p->left_y)); + lua_push_number_at_index(L, 5, mp_number_as_double(mp, p->right_x)); + lua_push_number_at_index(L, 6, mp_number_as_double(mp, p->right_y)); + lua_rawseti(L, -2, i++); + if (rt == mp_endpoint_knot) { + iscycle = 0; + break; + } else { + q = p; + p = p->next; + } + } while (p && p != h); + if (check && iscycle && i > 1 && aux_is_curved(mp, q, h, bendtolerance)) { + lua_rawgeti(L, -1, 1); + lua_push_boolean_at_key(L, curved, 1); + lua_pop(L, 1); + } + } + if (ispen) { + lua_push_boolean_at_key(L, pen, 1); + } + lua_push_boolean_at_key(L, cycle, iscycle); +} + +/*tex + + A couple of scanners. I know what I want to change but not now. First some longer term + experiments. + +*/ + +# define push_number_in_slot(L,i,n) \ + lua_pushnumber(L, n); \ + lua_rawseti(L, -2, i); + +# define kind_of_expression(n) \ + lmt_optinteger(L, n, 0) + +static int mplib_scan_string(lua_State *L) +{ + MP mp = mplib_aux_is_mp(L, 1); + if (mp) { + char *s = NULL; + size_t l = 0; + mp_scan_string_value(mp, kind_of_expression(2), &s, &l) ; + if (s) { + lua_pushlstring(L, s, l); + return 1; + } + } + lua_pushliteral(L,""); + return 1; +} + +static int mplib_scan_boolean(lua_State *L) +{ + MP mp = mplib_aux_is_mp(L, 1); + int b = 0; + if (mp) { + mp_scan_boolean_value(mp, kind_of_expression(2), &b); + } + lua_pushboolean(L, b); + return 1; +} + +static int mplib_scan_numeric(lua_State *L) +{ + MP mp = mplib_aux_is_mp(L, 1); + double d = 0.0; + if (mp) { + mp_scan_numeric_value(mp, kind_of_expression(2), &d); + } + lua_pushnumber(L, d); + return 1; +} + +static int mplib_scan_integer(lua_State *L) +{ + MP mp = mplib_aux_is_mp(L, 1); + double d = 0.0; + if (mp) { + mp_scan_numeric_value(mp, kind_of_expression(2), &d); + } + lua_pushinteger(L, (int) d); /* floored */ + return 1; +} + +static int mplib_scan_pair(lua_State *L) +{ + MP mp = mplib_aux_is_mp(L, 1); + double x = 0.0; + double y = 0.0; + if (mp) { + mp_scan_pair_value(mp, kind_of_expression(3), &x, &y); + } + if (lua_toboolean(L, 2)) { + lua_createtable(L, 2, 0); + push_number_in_slot(L, 1, x); + push_number_in_slot(L, 2, y); + return 1; + } else { + lua_pushnumber(L, x); + lua_pushnumber(L, y); + return 2; + } +} + +static int mplib_scan_color(lua_State *L) +{ + MP mp = mplib_aux_is_mp(L, 1); + double r = 0.0; + double g = 0.0; + double b = 0.0; + if (mp) { + mp_scan_color_value(mp, kind_of_expression(3), &r, &g, &b); + } + if (lua_toboolean(L, 2)) { + lua_createtable(L, 3, 0); + push_number_in_slot(L, 1, r); + push_number_in_slot(L, 2, g); + push_number_in_slot(L, 3, b); + return 1; + } else { + lua_pushnumber(L, r); + lua_pushnumber(L, g); + lua_pushnumber(L, b); + return 3; + } +} + +static int mplib_scan_cmykcolor(lua_State *L) +{ + MP mp = mplib_aux_is_mp(L, 1); + double c = 0.0; + double m = 0.0; + double y = 0.0; + double k = 0.0; + if (mp) { + mp_scan_cmykcolor_value(mp, kind_of_expression(3), &c, &m, &y, &k); + } + if (lua_toboolean(L, 2)) { + lua_createtable(L, 4, 0); + push_number_in_slot(L, 1, c); + push_number_in_slot(L, 2, m); + push_number_in_slot(L, 3, y); + push_number_in_slot(L, 4, k); + return 1; + } else { + lua_pushnumber(L, c); + lua_pushnumber(L, m); + lua_pushnumber(L, y); + lua_pushnumber(L, k); + return 4; + } +} + +static int mplib_scan_transform(lua_State *L) +{ + MP mp = mplib_aux_is_mp(L, 1); + double x = 0.0; + double y = 0.0; + double xx = 0.0; + double xy = 0.0; + double yx = 0.0; + double yy = 0.0; + if (mp) { + mp_scan_transform_value(mp, kind_of_expression(3), &x, &y, &xx, &xy, &yx, &yy); + } + if (lua_toboolean(L, 2)) { + lua_createtable(L, 6, 0); + push_number_in_slot(L, 1, x); + push_number_in_slot(L, 2, y); + push_number_in_slot(L, 3, xx); + push_number_in_slot(L, 4, xy); + push_number_in_slot(L, 5, yx); + push_number_in_slot(L, 6, yy); + return 1; + } else { + lua_pushnumber(L, x); + lua_pushnumber(L, y); + lua_pushnumber(L, xx); + lua_pushnumber(L, xy); + lua_pushnumber(L, yx); + lua_pushnumber(L, yy); + return 6; + } +} + +static int mplib_scan_path(lua_State *L) /* 1=mp 2=compact 3=kind(prim) 4=check */ +{ + MP mp = mplib_aux_is_mp(L, 1); + if (mp) { + mp_knot p = NULL; + lua_Number t = mplib_aux_get_bend_tolerance(L, 1); /* iuservalue */ + mp_scan_path_value(mp, kind_of_expression(3), &p); + if (p) { + aux_mplib_knot_to_path(L, mp, p, 0, lua_toboolean(L, 2), lua_toboolean(L, 4), t); + return 1; + } + } + return 0; +} + +static int mplib_scan_pen(lua_State *L) +{ + MP mp = mplib_aux_is_mp(L, 1); + if (mp) { + mp_knot p = NULL ; + lua_Number t = mplib_aux_get_bend_tolerance(L, 1); + mp_scan_path_value(mp, kind_of_expression(3), &p) ; + if (p) { + aux_mplib_knot_to_path(L, mp, p, 1, lua_toboolean(L, 2), lua_toboolean(L, 4), t); + return 1; + } + } + return 0; +} + +static int mplib_inject_string(lua_State *L) +{ + MP mp = mplib_aux_is_mp(L, 1); + if (mp) { + size_t l = 0; + const char *s = lua_tolstring(L, 2, &l); + mp_push_string_value(mp, s, (int) l); + } + return 0; +} + +static int mplib_inject_boolean(lua_State *L) +{ + MP mp = mplib_aux_is_mp(L, 1); + if (mp) { + int b = lua_toboolean(L, 2); + mp_push_boolean_value(mp, b); + } + return 0; +} + +static int mplib_inject_numeric(lua_State *L) +{ + MP mp = mplib_aux_is_mp(L, 1); + if (mp) { + double d = lua_tonumber(L, 2); + mp_push_numeric_value(mp, d); + } + return 0; +} + +static int mplib_inject_integer(lua_State *L) +{ + MP mp = mplib_aux_is_mp(L, 1); + if (mp) { + int i = lmt_tointeger(L, 2); + mp_push_integer_value(mp, i); + } + return 0; +} + +static int mplib_inject_pair(lua_State *L) +{ + MP mp = mplib_aux_is_mp(L, 1); + if (mp) { + switch (lua_type(L, 2)) { + case LUA_TNUMBER: + mp_push_pair_value(mp, + luaL_optnumber(L, 2, 0), + luaL_optnumber(L, 3, 0) + ); + break; + case LUA_TTABLE: + mp_push_pair_value(mp, + lmt_number_from_table(L, 2, 1, 0.0), + lmt_number_from_table(L, 2, 2, 0.0) + ); + break; + } + } + return 0; +} + +static int mplib_inject_color(lua_State *L) +{ + MP mp = mplib_aux_is_mp(L, 1); + if (mp) { + switch (lua_type(L, 2)) { + case LUA_TNUMBER: + mp_push_color_value(mp, + luaL_optnumber(L, 2, 0), + luaL_optnumber(L, 3, 0), + luaL_optnumber(L, 4, 0) + ); + break; + case LUA_TTABLE: + mp_push_color_value(mp, + lmt_number_from_table(L, 2, 1, 0.0), + lmt_number_from_table(L, 2, 2, 0.0), + lmt_number_from_table(L, 2, 3, 0.0) + ); + break; + } + } + return 0; +} + +static int mplib_inject_cmykcolor(lua_State *L) +{ + MP mp = mplib_aux_is_mp(L, 1); + if (mp) { + switch (lua_type(L, 2)) { + case LUA_TNUMBER: + mp_push_cmykcolor_value(mp, + luaL_optnumber(L, 2, 0), + luaL_optnumber(L, 3, 0), + luaL_optnumber(L, 4, 0), + luaL_optnumber(L, 5, 0) + ); + break; + case LUA_TTABLE: + mp_push_cmykcolor_value(mp, + lmt_number_from_table(L, 2, 1, 0.0), + lmt_number_from_table(L, 2, 2, 0.0), + lmt_number_from_table(L, 2, 3, 0.0), + lmt_number_from_table(L, 2, 4, 0.0) + ); + break; + } + } + return 0; +} + +static int mplib_inject_transform(lua_State *L) +{ + MP mp = mplib_aux_is_mp(L, 1); + if (mp) { + switch (lua_type(L, 2)) { + case LUA_TNUMBER: + mp_push_transform_value(mp, + luaL_optnumber(L, 2, 0), // 1 + luaL_optnumber(L, 3, 0), + luaL_optnumber(L, 4, 0), + luaL_optnumber(L, 5, 0), // 1 + luaL_optnumber(L, 6, 0), + luaL_optnumber(L, 7, 0) + ); + break; + case LUA_TTABLE: + mp_push_transform_value(mp, + lmt_number_from_table(L, 2, 1, 0.0), // 1.0 + lmt_number_from_table(L, 2, 2, 0.0), + lmt_number_from_table(L, 2, 3, 0.0), + lmt_number_from_table(L, 2, 4, 0.0), // 1.0 + lmt_number_from_table(L, 2, 5, 0.0), + lmt_number_from_table(L, 2, 6, 0.0) + ); + break; + } + } + return 0; +} + +static int mplib_new(lua_State *L) +{ + MP *mpud = lua_newuserdatauv(L, sizeof(MP *), 2); + if (mpud) { + MP mp = NULL; + struct MP_options *options = mp_options(); + lua_Number bendtolerance = default_bend_tolerance; + lua_Number movetolerance = default_move_tolerance; + options->userdata = (void *) L; + options->job_name = NULL; + options->extensions = 0 ; + options->utf8_mode = 0; + options->text_mode = 0; + options->show_mode = 0; + options->halt_on_error = 0; + options->find_file = mplib_aux_find_file; + options->run_script = mplib_aux_run_script; + options->run_internal = mplib_aux_run_internal; + options->run_logger = mplib_aux_run_logger; + options->run_overload = mplib_aux_run_overload; + options->run_error = mplib_aux_run_error; + options->make_text = mplib_aux_make_text; + options->open_file = mplib_aux_open_file; + options->close_file = mplib_aux_close_file; + options->read_file = mplib_aux_read_file; + options->write_file = mplib_aux_write_file; + options->shipout_backend = mplib_shipout_backend; + if (lua_type(L, 1) == LUA_TTABLE) { + lua_pushnil(L); + while (lua_next(L, 1)) { + if (lua_type(L, -2) == LUA_TSTRING) { + const char *s = lua_tostring(L, -2); + if (lua_key_eq(s, random_seed)) { + options->random_seed = (int) lua_tointeger(L, -1); + } else if (lua_key_eq(s, interaction)) { + options->interaction = luaL_checkoption(L, -1, "silent", mplib_interaction_options); + } else if (lua_key_eq(s, job_name)) { + // options->job_name = lmt_generic_strdup(lua_tostring(L, -1)); + options->job_name = lmt_memory_strdup(lua_tostring(L, -1)); + } else if (lua_key_eq(s, find_file)) { + if (mplib_aux_find_file_function(L, options)) { + tex_normal_warning("mplib", "find file: function expected"); + } + } else if (lua_key_eq(s, run_script)) { + if (mplib_aux_run_script_function(L, options)) { + tex_normal_warning("mplib", "run script: function expected"); + } + } else if (lua_key_eq(s, run_internal)) { + if (mplib_aux_run_internal_function(L, options)) { + tex_normal_warning("mplib", "run internal: function expected"); + } + } else if (lua_key_eq(s, make_text)) { + if (mplib_aux_make_text_function(L, options)) { + tex_normal_warning("mplib", "make text: function expected"); + } + } else if (lua_key_eq(s, extensions)) { + options->extensions = (int) lua_tointeger(L, -1); + } else if (lua_key_eq(s, math_mode)) { + options->math_mode = luaL_checkoption(L, -1, "scaled", mplib_math_options); + } else if (lua_key_eq(s, utf8_mode)) { + options->utf8_mode = (int) lua_toboolean(L, -1); + } else if (lua_key_eq(s, text_mode)) { + options->text_mode = (int) lua_toboolean(L, -1); + } else if (lua_key_eq(s, show_mode)) { + options->show_mode = (int) lua_toboolean(L, -1); + } else if (lua_key_eq(s, halt_on_error)) { + options->halt_on_error = (int) lua_toboolean(L, -1); + } else if (lua_key_eq(s, run_logger)) { + if (mplib_aux_run_logger_function(L, options)) { + tex_normal_warning("mplib", "run logger: function expected"); + } + } else if (lua_key_eq(s, run_overload)) { + if (mplib_aux_run_overload_function(L, options)) { + tex_normal_warning("mplib", "run overload: function expected"); + } + } else if (lua_key_eq(s, run_error)) { + if (mplib_aux_run_error_function(L, options)) { + tex_normal_warning("mplib", "run error: function expected"); + } + } else if (lua_key_eq(s, open_file)) { + if (mplib_aux_open_file_function(L, options)) { + tex_normal_warning("mplib", "open file: function expected"); + } + } else if (lua_key_eq(s, bend_tolerance)) { + bendtolerance = lua_tonumber(L, -1); + } else if (lua_key_eq(s, move_tolerance)) { + movetolerance = lua_tonumber(L, -1); + } + } + lua_pop(L, 1); + } + } + if (! options->job_name || ! *(options->job_name)) { + mp_memory_free(options); /* leaks */ + tex_normal_warning("mplib", "job_name is not set"); + goto BAD; + } + mp = mp_initialize(options); + mp_memory_free(options); /* leaks */ + if (mp) { + *mpud = mp; + mplib_aux_set_bend_tolerance(L, bendtolerance); + mplib_aux_set_move_tolerance(L, movetolerance); + luaL_getmetatable(L, MP_METATABLE_INSTANCE); + lua_setmetatable(L, -2); + return 1; + } + } + BAD: + lua_pushnil(L); + return 1; +} + +# define mplib_collect_id(id) do { \ + if (id) { \ + luaL_unref(L, LUA_REGISTRYINDEX, id); \ + } \ +} while(0) + +static int mplib_instance_collect(lua_State *L) +{ + MP *mpud = mplib_aux_is_mpud(L, 1); + if (*mpud) { + MP mp = *mpud; + int run_logger_id = (mp)->run_logger_id; + mplib_collect_id((mp)->find_file_id); + mplib_collect_id((mp)->run_script_id); + mplib_collect_id((mp)->run_internal_id); + mplib_collect_id((mp)->run_overload_id); + mplib_collect_id((mp)->run_error_id); + mplib_collect_id((mp)->make_text_id); + mplib_collect_id((mp)->open_file_id); + mp_finish(mp); + *mpud = NULL; + mplib_collect_id(run_logger_id); + } + return 0; +} + +static int mplib_instance_tostring(lua_State *L) +{ + MP mp = mplib_aux_is_mp(L, 1); + if (mp) { + lua_pushfstring(L, "<mp.instance %p>", mp); + } else { + lua_pushnil(L); + } + return 1; +} + +static int mplib_aux_wrapresults(lua_State *L, mp_run_data *res, int status, lua_Number bendtolerance, lua_Number movetolerance) +{ + lua_newtable(L); + if (res->edges) { + struct mp_edge_object *p = res->edges; + int i = 1; + lua_push_key(fig); + lua_newtable(L); + while (p) { + struct mp_edge_object **v = lua_newuserdatauv(L, sizeof(struct mp_edge_object *), 2); + *v = p; + mplib_aux_set_bend_tolerance(L, bendtolerance); + mplib_aux_set_move_tolerance(L, movetolerance); + luaL_getmetatable(L, MP_METATABLE_FIGURE); + lua_setmetatable(L, -2); + lua_rawseti(L, -2, i); + i++; + p = p->next; + } + lua_rawset(L,-3); + res->edges = NULL; + } + lua_push_integer_at_key(L, status, status); + return 1; +} + +static int mplib_execute(lua_State *L) +{ + MP mp = mplib_aux_is_mp(L, 1); + if (mp) { + /* no string in slot 2 or an empty string means that we already filled the terminal */ + size_t l = 0; + lua_Number bendtolerance = mplib_aux_get_bend_tolerance(L, 1); + lua_Number movetolerance = mplib_aux_get_move_tolerance(L, 1); + const char *s = lua_isstring(L, 2) ? lua_tolstring(L, 2, &l) : NULL; + int h = mp_execute(mp, s, l); + mp_run_data *res = mp_rundata(mp); + return mplib_aux_wrapresults(L, res, h, bendtolerance, movetolerance); + } + lua_pushnil(L); + return 1; +} + +static int mplib_finish(lua_State *L) +{ + MP *mpud = mplib_aux_is_mpud(L, 1); + if (*mpud) { + MP mp = *mpud; + lua_Number bendtolerance = mplib_aux_get_bend_tolerance(L, 1); + lua_Number movetolerance = mplib_aux_get_move_tolerance(L, 1); + int h = mp_execute(mp, NULL, 0); + mp_run_data *res = mp_rundata(mp); + int i = mplib_aux_wrapresults(L, res, h, bendtolerance, movetolerance); + mp_finish(mp); + *mpud = NULL; + return i; + } else { + lua_pushnil(L); + } + return 1; +} + +static int mplib_showcontext(lua_State *L) +{ + MP *mpud = mplib_aux_is_mpud(L, 1); + if (*mpud) { + MP mp = *mpud; + mp_show_context(mp); + } + return 0; +} + +static int mplib_gethashentry(lua_State *L) +{ + MP *mpud = mplib_aux_is_mpud(L, 1); + if (*mpud) { + MP mp = *mpud; + char *name = (char *) lua_tostring(L, 2); + if (name) { + mp_symbol_entry *s = (mp_symbol_entry *) mp_fetch_symbol(mp, name); + if (s) { + mp_node q = s->type == mp_tag_command ? s->v.data.node : NULL; + lua_pushinteger(L, s->type); + lua_pushinteger(L, s->property); + if (q) { + lua_pushinteger(L, q->type); + return 3; + } else { + return 2; + } + } + } + } + return 0; +} + +static int mplib_gethashentries(lua_State *L) +{ + MP *mpud = mplib_aux_is_mpud(L, 1); + if (*mpud) { + MP mp = *mpud; + int full = lua_toboolean(L, 2); + if (mp_initialize_symbol_traverse(mp)) { + size_t n = 0; + lua_newtable(L); + while (1) { + mp_symbol_entry *s = (mp_symbol_entry *) mp_fetch_symbol_traverse(mp); + if (s) { + if (full) { + mp_node q = s->type == mp_tag_command ? s->v.data.node : NULL; + lua_createtable(L, (q || s->property == 0x1) ? 4 : 3, 0); + lua_pushinteger(L, s->type); + lua_rawseti(L, -2, 1); + lua_pushinteger(L, s->property); + lua_rawseti(L, -2, 2); + lua_pushlstring(L, (const char *) s->text->str, s->text->len); + lua_rawseti(L, -2, 3); + if (q) { + lua_pushinteger(L, q->type); + lua_rawseti(L, -2, 4); + } else if (s->property == 0x1) { + lua_pushinteger(L, s->v.data.indep.serial); + lua_rawseti(L, -2, 4); + } + } else { + lua_pushlstring(L, (const char *) s->text->str, s->text->len); + } + lua_rawseti(L, -2, ++n); + } else { + break; + } + } + mp_kill_symbol_traverse(mp); + return 1; + } + } + return 0; +} + +static int mplib_version(lua_State *L) +{ + char *s = mp_metapost_version(); + lua_pushstring(L, s); + mp_memory_free(s); + return 1; +} + +static int mplib_getstatistics(lua_State *L) +{ + MP mp = mplib_aux_is_mp(L, 1); + if (mp) { + lua_createtable(L, 0, 9); + lua_push_integer_at_key(L, memory, mp->var_used); /* bytes of node memory */ + lua_push_integer_at_key(L, hash, mp->st_count); + lua_push_integer_at_key(L, parameters, mp->max_param_stack); /* allocated: mp->param_size */ + lua_push_integer_at_key(L, input, mp->max_in_stack); /* allocated: mp->stack_size */ + lua_push_integer_at_key(L, tokens, mp->num_token_nodes); + lua_push_integer_at_key(L, pairs, mp->num_pair_nodes); + lua_push_integer_at_key(L, knots, mp->num_knot_nodes); + lua_push_integer_at_key(L, nodes, mp->num_value_nodes); + lua_push_integer_at_key(L, symbols, mp->num_symbolic_nodes); + lua_push_integer_at_key(L, characters, mp->max_pl_used); + lua_push_integer_at_key(L, strings, mp->max_strs_used); + lua_push_integer_at_key(L, internals, mp->int_ptr); /* allocates: mp->max_internal */ + /* lua_push_integer_at_key(L, buffer, mp->max_buf_stack + 1); */ /* allocated: mp->buf_size */ + /* lua_push_integer_at_key(L, open, mp->in_open_max - file_bottom); */ /* allocated: mp->max_in_open - file_bottom */ + } else { + lua_pushnil(L); + } + return 1; +} + +static int mplib_getstatus(lua_State *L) +{ + MP mp = mplib_aux_is_mp(L, 1); + if (mp) { + lua_pushinteger(L, mp->scanner_status); + return 1; + } else { + return 0; + } +} + +static int mplib_aux_set_direction(lua_State *L, MP mp, mp_knot p) { + double direction_x = (double) lua_tonumber(L, -1); + double direction_y = 0; + lua_pop(L, 1); + lua_push_key(direction_y); + if (lua_rawget(L, -2) == LUA_TNUMBER) { + direction_y = (double) lua_tonumber(L, -1); + lua_pop(L, 1); + return mp_set_knot_direction(mp, p, direction_x, direction_y) ? 1 : 0; + } else { + return 0; + } +} + +static int mplib_aux_set_left_curl(lua_State *L, MP mp, mp_knot p) { + double curl = (double) lua_tonumber(L, -1); + lua_pop(L, 1); + return mp_set_knot_left_curl(mp, p, curl) ? 1 : 0; +} + +static int mplib_aux_set_left_tension(lua_State *L, MP mp, mp_knot p) { + double tension = (double) lua_tonumber(L, -1); + lua_pop(L, 1); + return mp_set_knot_left_tension(mp, p, tension) ? 1 : 0; +} + +static int mplib_aux_set_left_control(lua_State *L, MP mp, mp_knot p) { + double x = (double) lua_tonumber(L, -1); + double y = 0; + lua_pop(L, 1); + lua_push_key(left_y); + if (lua_rawget(L, -2) == LUA_TNUMBER) { + y = (double) lua_tonumber(L, -1); + lua_pop(L, 1); + return mp_set_knot_left_control(mp, p, x, y) ? 1 : 0; + } else { + return 0; + } +} + +static int mplib_aux_set_right_curl(lua_State *L, MP mp, mp_knot p) { + double curl = (double) lua_tonumber(L, -1); + lua_pop(L, 1); + return mp_set_knot_right_curl(mp, p, curl) ? 1 : 0; +} + +static int mplib_aux_set_right_tension(lua_State *L, MP mp, mp_knot p) { + double tension = (double) lua_tonumber(L, -1); + lua_pop(L, 1); + return mp_set_knot_right_tension(mp, p, tension) ? 1 : 0; +} + +static int mplib_aux_set_right_control(lua_State *L, MP mp, mp_knot p) { + double x = (double) lua_tonumber(L, -1); + lua_pop(L, 1); + lua_push_key(right_y); + if (lua_rawget(L, -2) == LUA_TNUMBER) { + double y = (double) lua_tonumber(L, -1); + lua_pop(L, 1); + return mp_set_knot_right_control(mp, p, x, y) ? 1 : 0; + } else { + return 0; + } +} + +static int mplib_aux_with_path(lua_State *L, MP mp, int index, int inject, int multiple) +{ + // setbuf(stdout, NULL); + if (! mp) { + lua_pushboolean(L, 0); + lua_pushstring(L, "valid instance expected"); + return 2; + } else if (! lua_istable(L, index) || lua_rawlen(L, index) <= 0) { + lua_pushboolean(L, 0); + lua_pushstring(L, "non empty table expected"); + return 2; + } else { + mp_knot p = NULL; + mp_knot q = NULL; + mp_knot first = NULL; + const char *errormsg = NULL; + int cyclic = 0; + int curled = 0; + int solve = 0; + int numpoints = (int) lua_rawlen(L, index); + /*tex + As a bonus we check for two keys. When an index is negative we come from the + callback in which case we definitely cannot check the rest of the arguments. + */ + if (multiple && lua_type(L, index + 1) == LUA_TBOOLEAN) { + cyclic = lua_toboolean(L, index + 1); + } else { + lua_push_key(close); + if (lua_rawget(L, index - 1) == LUA_TBOOLEAN) { + cyclic = lua_toboolean(L, -1); + } + lua_pop(L, 1); + lua_push_key(cycle); /* wins */ + if (lua_rawget(L, index - 1) == LUA_TBOOLEAN) { + cyclic = lua_toboolean(L, -1); + } + lua_pop(L, 1); + } + if (multiple && lua_type(L, index + 2) == LUA_TBOOLEAN) { + curled = lua_toboolean(L, index + 2); + } else { + lua_push_key(curled); + if (lua_rawget(L, index - 1) == LUA_TBOOLEAN) { + curled = lua_toboolean(L, -1); + } + lua_pop(L, 1); + } + /*tex We build up the path. */ + if (lua_rawgeti(L, index, 1) == LUA_TTABLE) { + lua_Unsigned len = lua_rawlen(L, -1); + lua_pop(L, 1); + if (len >= 2) { + /* .. : p1 .. controls a and b .. p2 : { p1 a b } */ + /* -- : p1 .. { curl 1 } .. { curl 1 } .. p2 : { p1 nil nil } */ + for (int i = 1; i <= numpoints; i++) { + if (lua_rawgeti(L, index, i) == LUA_TTABLE) { + double x0, y0; + lua_rawgeti(L, -1, 1); + lua_rawgeti(L, -2, 2); + x0 = lua_tonumber(L, -2); + y0 = lua_tonumber(L, -1); + q = p; + p = mp_append_knot_xy(mp, p, x0, y0); /* makes end point */ + lua_pop(L, 2); + if (p) { + double x1, y1, x2, y2; + if (curled) { + x1 = x0; + y1 = y0; + x2 = x0; + y2 = y0; + } else { + lua_rawgeti(L, -1, 3); + lua_rawgeti(L, -2, 4); + lua_rawgeti(L, -3, 5); + lua_rawgeti(L, -4, 6); + x1 = luaL_optnumber(L, -4, x0); + y1 = luaL_optnumber(L, -3, y0); + x2 = luaL_optnumber(L, -2, x0); + y2 = luaL_optnumber(L, -1, y0); + lua_pop(L, 4); + } + mp_set_knot_left_control(mp, p, x1, y1); + mp_set_knot_right_control(mp, p, x2, y2); + if (! first) { + first = p; + } + } else { + errormsg = "knot creation failure"; + goto BAD; + } + } + /*tex Up the next item */ + lua_pop(L, 1); + } + } else if (len > 0) { + errormsg = "messy table"; + goto BAD; + } else { + for (int i = 1; i <= numpoints; i++) { + if (lua_rawgeti(L, index, i) == LUA_TTABLE) { + /* We can probably also use the _xy here. */ + int left_set = 0; + int right_set = 0; + double x_coord, y_coord; + if (! lua_istable(L, -1)) { + errormsg = "wrong argument types"; + goto BAD; + } + lua_push_key(x_coord); + if (lua_rawget(L, -2) != LUA_TNUMBER) { + errormsg = "missing x coordinate"; + goto BAD; + } + x_coord = (double) lua_tonumber(L, -1); + lua_pop(L, 1); + lua_push_key(y_coord); + if (lua_rawget(L, -2) != LUA_TNUMBER) { + errormsg = "missing y coordinate"; + goto BAD; + } + y_coord = (double) lua_tonumber(L, -1); + lua_pop(L, 1); + /* */ + q = p; + if (q) { + /*tex + We have to save the right_tension because |mp_append_knot| trashes it, + believing that it is as yet uninitialized .. I need to check this. + */ + double saved_tension = mp_number_as_double(mp, p->right_tension); + p = mp_append_knot(mp, p, x_coord, y_coord); + if (p) { + mp_set_knot_right_tension(mp, q, saved_tension); + } + } else { + p = mp_append_knot(mp, p, x_coord, y_coord); + } + if (p) { + errormsg = "knot creation failure"; + goto BAD; + } + /* */ + if (! first) { + first = p; + } + lua_push_key(left_curl); + if (lua_rawget(L, -2) != LUA_TNUMBER) { + lua_pop(L, 1); + } else if (! mplib_aux_set_left_curl(L, mp, p)) { + errormsg = "failed to set left curl"; + goto BAD; + } else { + left_set = 1; + solve = 1; + } + lua_push_key(left_tension); + if (lua_rawget(L, -2) != LUA_TNUMBER) { + lua_pop(L, 1); + } else if (left_set) { + errormsg = "left side already set"; + goto BAD; + } else if (! mplib_aux_set_left_tension(L, mp, p)) { + errormsg = "failed to set left tension"; + goto BAD; + } else { + left_set = 1; + solve = 1; + } + lua_push_key(left_x); + if (lua_rawget(L, -2) != LUA_TNUMBER) { + lua_pop(L, 1); + } else if (left_set) { + errormsg = "left side already set"; + goto BAD; + } else if (! mplib_aux_set_left_control(L, mp, p)) { + errormsg = "failed to set left control"; + goto BAD; + } + lua_push_key(right_curl); + if (lua_rawget(L, -2) != LUA_TNUMBER) { + lua_pop(L, 1); + } else if (! mplib_aux_set_right_curl(L, mp, p)) { + errormsg = "failed to set right curl"; + goto BAD; + } else { + right_set = 1; + solve = 1; + } + lua_push_key(right_tension); + if (lua_rawget(L, -2) != LUA_TNUMBER) { + lua_pop(L,1); + } else if (right_set) { + errormsg = "right side already set"; + goto BAD; + } else if (! mplib_aux_set_right_tension(L, mp, p)) { + errormsg = "failed to set right tension"; + goto BAD; + } else { + right_set = 1; + solve = 1; + } + lua_push_key(right_x); + if (lua_rawget(L, -2) != LUA_TNUMBER) { + lua_pop(L, 1); + } else if (right_set) { + errormsg = "right side already set"; + goto BAD; + } else if (! mplib_aux_set_right_control(L, mp, p)) { + errormsg = "failed to set right control"; + goto BAD; + } + lua_push_key(direction_x); + if (lua_rawget(L, -2) != LUA_TNUMBER) { + lua_pop(L, 1); + } else if (! mplib_aux_set_direction(L, mp, p)) { + errormsg = "failed to set direction"; + goto BAD; + } + } + lua_pop(L, 1); + } + } + } + if (first && p) { + /* not: mp_close_path(mp, p, first); */ + if (cyclic) { + p->right_type = mp_explicit_knot; + first->left_type = mp_explicit_knot; + } else { + /* check this on shapes-001.tex and arrows-001.tex */ + p->right_type = mp_endpoint_knot; + first->left_type = mp_endpoint_knot; + } + p->next = first; + if (inject) { + if (solve && ! mp_solve_path(mp, first)) { + tex_normal_warning("lua", "failed to solve the path"); + } + mp_push_path_value(mp, first); + return 0; + } else { + /*tex We're finished reading arguments so we squeeze the new values back into the table. */ + if (! mp_solve_path(mp, first)) { + errormsg = "failed to solve the path"; + } else { + /* We replace in the original table .. maybe not a good idea at all. */ + p = first; + for (int i = 1; i <= numpoints; i++) { + lua_rawgeti(L, -1, i); + lua_push_number_at_key(L, left_x, mp_number_as_double(mp, p->left_x)); + lua_push_number_at_key(L, left_y, mp_number_as_double(mp, p->left_y)); + lua_push_number_at_key(L, right_x, mp_number_as_double(mp, p->right_x)); + lua_push_number_at_key(L, right_y, mp_number_as_double(mp, p->right_y)); + /*tex This is a bit overkill, wiping \unknown */ + lua_push_nil_at_key(L, left_tension); + lua_push_nil_at_key(L, right_tension); + lua_push_nil_at_key(L, left_curl); + lua_push_nil_at_key(L, right_curl); + lua_push_nil_at_key(L, direction_x); + lua_push_nil_at_key(L, direction_y); + /*tex \unknown\ till here. */ + lua_push_svalue_at_key(L, left_type, mplib_values_knot[p->left_type]); + lua_push_svalue_at_key(L, right_type, mplib_values_knot[p->right_type]); + lua_pop(L, 1); + p = p->next; + } + lua_pushboolean(L, 1); + return 1; + } + } + } else { + errormsg = "invalid path"; + } + BAD: + if (p) { + mp_free_path(mp, p); + } + lua_pushboolean(L, 0); + if (errormsg) { + lua_pushstring(L, errormsg); + return 2; + } else { + return 1; + } + } +} + +static int mplib_solvepath(lua_State *L) +{ + MP mp = mplib_aux_is_mp(L, 1); + if (mp) { + return mplib_aux_with_path(L, mp, 2, 0, 1); + } else { + return 0; + } +} + +static int mplib_inject_path(lua_State *L) +{ + MP mp = mplib_aux_is_mp(L, 1); + if (mp) { + return mplib_aux_with_path(L, mp, 2, 1, 1); + } else { + return 0; + } +} + +static int mplib_inject_whatever(lua_State *L) +{ + MP mp = mplib_aux_is_mp(L, 1); + if (mp) { + mplib_aux_inject_whatever(L, mp, 2); + } + return 0; +} + +/*tex The next methods are for collecting the results from |fig|. */ + +static int mplib_figure_collect(lua_State *L) +{ + struct mp_edge_object **hh = mplib_aux_is_figure(L, 1); + if (*hh) { + mp_gr_toss_objects(*hh); + *hh = NULL; + } + return 0; +} + +static int mplib_figure_objects(lua_State *L) +{ + struct mp_edge_object **hh = mplib_aux_is_figure(L, 1); + if (*hh) { + int i = 1; + struct mp_graphic_object *p = (*hh)->body; + lua_Number bendtolerance = mplib_aux_get_bend_tolerance(L, 1); + lua_Number movetolerance = mplib_aux_get_move_tolerance(L, 1); + lua_newtable(L); + while (p) { + struct mp_graphic_object **v = lua_newuserdatauv(L, sizeof(struct mp_graphic_object *), 2); + *v = p; + mplib_aux_set_bend_tolerance(L, bendtolerance); + mplib_aux_set_move_tolerance(L, movetolerance); + luaL_getmetatable(L, MP_METATABLE_OBJECT); + lua_setmetatable(L, -2); + lua_rawseti(L, -2, i); + i++; + p = p->next; + } + /*tex Prevent a double free: */ + (*hh)->body = NULL; + } else { + lua_pushnil(L); + } + return 1; +} + +static int mplib_figure_stacking(lua_State *L) +{ + struct mp_edge_object **hh = mplib_aux_is_figure(L, 1); + int stacking = 0; /* This only works when before fetching objects! */ + if (*hh) { + struct mp_graphic_object *p = (*hh)->body; + while (p) { + if (((mp_shape_object *) p)->stacking) { + stacking = 1; + break; + } else { + p = p->next; + } + } + } + lua_pushboolean(L, stacking); + return 1; +} + +static int mplib_figure_tostring(lua_State *L) +{ + struct mp_edge_object **hh = mplib_aux_is_figure(L, 1); + if (*hh) { + lua_pushfstring(L, "<mp.figure %p>", *hh); + } else { + lua_pushnil(L); + } + return 1; +} + +static int mplib_figure_width(lua_State *L) +{ + struct mp_edge_object **hh = mplib_aux_is_figure(L, 1); + if (*hh) { + lua_pushnumber(L, (*hh)->width); + } else { + lua_pushnil(L); + } + return 1; +} + +static int mplib_figure_height(lua_State *L) +{ + struct mp_edge_object **hh = mplib_aux_is_figure(L, 1); + if (*hh) { + lua_pushnumber(L, (*hh)->height); + } else { + lua_pushnil(L); + } + return 1; +} + +static int mplib_figure_depth(lua_State *L) +{ + struct mp_edge_object **hh = mplib_aux_is_figure(L, 1); + if (*hh) { + lua_pushnumber(L, (*hh)->depth); + } else { + lua_pushnil(L); + } + return 1; +} + +static int mplib_figure_italic(lua_State *L) +{ + struct mp_edge_object **hh = mplib_aux_is_figure(L, 1); + if (*hh) { + lua_pushnumber(L, (*hh)->italic); + } else { + lua_pushnil(L); + } + return 1; +} + +static int mplib_figure_charcode(lua_State *L) +{ + struct mp_edge_object **hh = mplib_aux_is_figure(L, 1); + if (*hh) { + lua_pushinteger(L, (lua_Integer) (*hh)->charcode); + } else { + lua_pushnil(L); + } + return 1; +} + +static int mplib_figure_tolerance(lua_State *L) +{ + struct mp_edge_object **hh = mplib_aux_is_figure(L, 1); + if (*hh) { + lua_pushnumber(L, mplib_aux_get_bend_tolerance(L, 1)); + lua_pushnumber(L, mplib_aux_get_move_tolerance(L, 1)); + } else { + lua_pushnil(L); + lua_pushnil(L); + } + return 2; +} + +static int mplib_figure_bounds(lua_State *L) +{ + struct mp_edge_object **hh = mplib_aux_is_figure(L, 1); + lua_createtable(L, 4, 0); + lua_push_number_at_index(L, 1, (*hh)->minx); + lua_push_number_at_index(L, 2, (*hh)->miny); + lua_push_number_at_index(L, 3, (*hh)->maxx); + lua_push_number_at_index(L, 4, (*hh)->maxy); + return 1; +} + +/*tex The methods for the figure objects plus a few helpers. */ + +static int mplib_object_collect(lua_State *L) +{ + struct mp_graphic_object **hh = mplib_aux_is_gr_object(L, 1); + if (*hh) { + mp_gr_toss_object(*hh); + *hh = NULL; + } + return 0; +} + +static int mplib_object_tostring(lua_State *L) +{ + struct mp_graphic_object **hh = mplib_aux_is_gr_object(L, 1); + lua_pushfstring(L, "<mp.object %p>", *hh); + return 1; +} + +# define pyth(a,b) (sqrt((a)*(a) + (b)*(b))) +# define aspect_bound (10.0/65536.0) +# define aspect_default 1.0 +# define eps 0.0001 + +static double mplib_aux_coord_range_x(mp_gr_knot h, double dz) +{ + double zlo = 0.0; + double zhi = 0.0; + mp_gr_knot f = h; + while (h) { + double z = h->x_coord; + if (z < zlo) { + zlo = z; + } else if (z > zhi) { + zhi = z; + } + z = h->right_x; + if (z < zlo) { + zlo = z; + } else if (z > zhi) { + zhi = z; + } + z = h->left_x; + if (z < zlo) { + zlo = z; + } else if (z > zhi) { + zhi = z; + } + h = h->next; + if (h == f) { + break; + } + } + return (zhi - zlo <= dz) ? aspect_bound : aspect_default; +} + +static double mplib_aux_coord_range_y(mp_gr_knot h, double dz) +{ + double zlo = 0.0; + double zhi = 0.0; + mp_gr_knot f = h; + while (h) { + double z = h->y_coord; + if (z < zlo) { + zlo = z; + } else if (z > zhi) { + zhi = z; + } + z = h->right_y; + if (z < zlo) { + zlo = z; + } else if (z > zhi) { + zhi = z; + } + z = h->left_y; + if (z < zlo) { + zlo = z; + } else if (z > zhi) { + zhi = z; + } + h = h->next; + if (h == f) { + break; + } + } + return (zhi - zlo <= dz) ? aspect_bound : aspect_default; +} + +static int mplib_object_peninfo(lua_State *L) +{ + struct mp_graphic_object **hh = mplib_aux_is_gr_object(L, -1); + if (! *hh) { + lua_pushnil(L); + return 1; + } else { + mp_gr_knot p = NULL; + mp_gr_knot path = NULL; + switch ((*hh)->type) { + case mp_fill_code: + case mp_stroked_code: + p = ((mp_shape_object *) (*hh))->pen; + path = ((mp_shape_object *) (*hh))->path; + break; + } + if (! p || ! path) { + lua_pushnil(L); + return 1; + } else { + double wx, wy; + double rx = 1.0, sx = 0.0, sy = 0.0, ry = 1.0, tx = 0.0, ty = 0.0; + double width = 1.0; + double x_coord = p->x_coord; + double y_coord = p->y_coord; + double left_x = p->left_x; + double left_y = p->left_y; + double right_x = p->right_x; + double right_y = p->right_y; + if ((right_x == x_coord) && (left_y == y_coord)) { + wx = fabs(left_x - x_coord); + wy = fabs(right_y - y_coord); + } else { + wx = pyth(left_x - x_coord, right_x - x_coord); + wy = pyth(left_y - y_coord, right_y - y_coord); + } + if ((wy/mplib_aux_coord_range_x(path, wx)) >= (wx/mplib_aux_coord_range_y(path, wy))) { + width = wy; + } else { + width = wx; + } + tx = x_coord; + ty = y_coord; + sx = left_x - tx; + rx = left_y - ty; + ry = right_x - tx; + sy = right_y - ty; + if (width != 1.0) { + if (width == 0.0) { + sx = 1.0; + sy = 1.0; + } else { + rx /= width; + ry /= width; + sx /= width; + sy /= width; + } + } + if (fabs(sx) < eps) { + sx = eps; + } + if (fabs(sy) < eps) { + sy = eps; + } + lua_createtable(L,0,7); + lua_push_number_at_key(L, width, width); + lua_push_number_at_key(L, rx, rx); + lua_push_number_at_key(L, sx, sx); + lua_push_number_at_key(L, sy, sy); + lua_push_number_at_key(L, ry, ry); + lua_push_number_at_key(L, tx, tx); + lua_push_number_at_key(L, ty, ty); + return 1; + } + } +} + +/*tex Here is a helper that reports the valid field names of the possible objects. */ + +static void mplib_aux_mplib_push_fields(lua_State* L, const char **fields) +{ + lua_newtable(L); + for (lua_Integer i = 0; fields[i]; i++) { + lua_pushstring(L, fields[i]); /* not yet an index */ + lua_rawseti(L, -2, i + 1); + } +} + +static int mplib_gettype(lua_State *L) +{ + struct mp_graphic_object **hh = mplib_aux_is_gr_object(L, 1); + if (*hh) { + lua_pushinteger(L, (*hh)->type); + } else { + lua_pushnil(L); + } + return 1; +} + +static int mplib_getobjecttypes(lua_State* L) +{ + lua_createtable(L, 7, 1); + lua_push_key_at_index(L, fill, mp_fill_code); + lua_push_key_at_index(L, outline, mp_stroked_code); + lua_push_key_at_index(L, start_clip, mp_start_clip_code); + lua_push_key_at_index(L, start_group, mp_start_group_code); + lua_push_key_at_index(L, start_bounds, mp_start_bounds_code); + lua_push_key_at_index(L, stop_clip, mp_stop_clip_code); + lua_push_key_at_index(L, stop_group, mp_stop_group_code); + lua_push_key_at_index(L, stop_bounds, mp_stop_bounds_code); + return 1; +} + +static int mplib_getfields(lua_State *L) +{ + if (lua_type(L, 1) == LUA_TUSERDATA) { + struct mp_graphic_object **hh = mplib_aux_is_gr_object(L, 1); + if (*hh) { + const char **fields; + switch ((*hh)->type) { + case mp_fill_code : fields = mplib_fill_fields; break; + case mp_stroked_code : fields = mplib_stroked_fields; break; + case mp_start_clip_code : fields = mplib_start_clip_fields; break; + case mp_start_group_code : fields = mplib_start_group_fields; break; + case mp_start_bounds_code: fields = mplib_start_bounds_fields; break; + case mp_stop_clip_code : fields = mplib_stop_clip_fields; break; + case mp_stop_group_code : fields = mplib_stop_group_fields; break; + case mp_stop_bounds_code : fields = mplib_stop_bounds_fields; break; + default : fields = mplib_no_fields; break; + } + mplib_aux_mplib_push_fields(L, fields); + } else { + lua_pushnil(L); + } + } else { + lua_createtable(L, 8, 0); + mplib_aux_mplib_push_fields(L, mplib_fill_fields); lua_rawseti(L, -2, mp_fill_code); + mplib_aux_mplib_push_fields(L, mplib_stroked_fields); lua_rawseti(L, -2, mp_stroked_code); + mplib_aux_mplib_push_fields(L, mplib_start_clip_fields); lua_rawseti(L, -2, mp_start_clip_code); + mplib_aux_mplib_push_fields(L, mplib_start_group_fields); lua_rawseti(L, -2, mp_start_group_code); + mplib_aux_mplib_push_fields(L, mplib_start_bounds_fields); lua_rawseti(L, -2, mp_start_bounds_code); + mplib_aux_mplib_push_fields(L, mplib_stop_clip_fields); lua_rawseti(L, -2, mp_stop_clip_code); + mplib_aux_mplib_push_fields(L, mplib_stop_group_fields); lua_rawseti(L, -2, mp_stop_group_code); + mplib_aux_mplib_push_fields(L, mplib_stop_bounds_fields); lua_rawseti(L, -2, mp_stop_bounds_code); + } + return 1; +} + +static int mplib_push_values(lua_State *L, const char *list[]) +{ + lua_newtable(L); + for (lua_Integer i = 0; list[i]; i++) { + lua_pushstring(L, list[i]); + lua_rawseti(L, -2, i); + } + return 1; +} + +static int mplib_getcodes(lua_State *L) +{ + return mplib_push_values(L, mplib_codes); +} + +static int mplib_gettypes(lua_State *L) +{ + return mplib_push_values(L, mplib_types); +} + +static int mplib_getcolormodels(lua_State *L) +{ + return mplib_push_values(L, mplib_colormodels); +} + +static int mplib_getstates(lua_State *L) +{ + return mplib_push_values(L, mplib_states); +} + +static int mplib_getcallbackstate(lua_State *L) +{ + lua_createtable(L, 0, 5); + lua_push_integer_at_key(L, file, mplib_state.file_callbacks); + lua_push_integer_at_key(L, text, mplib_state.text_callbacks); + lua_push_integer_at_key(L, script, mplib_state.script_callbacks); + lua_push_integer_at_key(L, log, mplib_state.log_callbacks); + lua_push_integer_at_key(L, overloaded, mplib_state.overload_callbacks); + lua_push_integer_at_key(L, error, mplib_state.error_callbacks); + lua_push_integer_at_key(L, warning, mplib_state.warning_callbacks); + lua_push_integer_at_key(L, count, + mplib_state.file_callbacks + mplib_state.text_callbacks + + mplib_state.script_callbacks + mplib_state.log_callbacks + + mplib_state.overload_callbacks + mplib_state.error_callbacks + + mplib_state.warning_callbacks + ); + return 1; +} + +/*tex + + This assumes that the top of the stack is a table or nil already in the case. +*/ + +# define mplib_set_color_objects(pq) \ +object_color_model = pq->color_model; \ +object_color_a = pq->color.a_val; \ +object_color_b = pq->color.b_val; \ +object_color_c = pq->color.c_val; \ +object_color_d = pq->color.d_val; + +static void mplib_aux_push_color(lua_State *L, struct mp_graphic_object *p) +{ + if (p) { + int object_color_model; + double object_color_a, object_color_b, object_color_c, object_color_d; + switch (p->type) { + case mp_fill_code: + case mp_stroked_code: + { + mp_shape_object *h = (mp_shape_object *) p; + mplib_set_color_objects(h); + } + break; + default: + object_color_model = mp_no_model; + break; + } + switch (object_color_model) { + case mp_grey_model: + lua_createtable(L, 1, 0); + lua_push_number_at_index(L, 1, object_color_d); + break; + case mp_rgb_model: + lua_createtable(L, 3, 0); + lua_push_number_at_index(L, 1, object_color_a); + lua_push_number_at_index(L, 2, object_color_b); + lua_push_number_at_index(L, 3, object_color_c); + break; + case mp_cmyk_model: + lua_createtable(L, 4, 0); + lua_push_number_at_index(L, 1, object_color_a); + lua_push_number_at_index(L, 2, object_color_b); + lua_push_number_at_index(L, 3, object_color_c); + lua_push_number_at_index(L, 4, object_color_d); + break; + default: + lua_pushnil(L); + break; + } + } else { + lua_pushnil(L); + } +} + +/*tex The dash scale is not exported, the field has no external value. */ + +static void mplib_aux_push_dash(lua_State *L, struct mp_shape_object *h) +{ + if (h && h->dash) { + mp_dash_object *d = h->dash; + lua_newtable(L); /* we could start at size 2 or so */ + lua_push_number_at_key(L, offset, d->offset); + if (d->array) { + int i = 0; + lua_push_key(dashes); + lua_newtable(L); + while (*(d->array + i) != -1) { + double ds = *(d->array + i); + lua_pushnumber(L, ds); + i++; + lua_rawseti(L, -2, i); + } + lua_rawset(L, -3); + } + } else { + lua_pushnil(L); + } +} + +static void mplib_aux_shape(lua_State *L, const char *s, struct mp_shape_object *h, lua_Number bendtolerance, lua_Number movetolerance) +{ + if (lua_key_eq(s, path)) { + mplib_aux_push_path(L, h->path, MPLIB_PATH, bendtolerance, movetolerance); + } else if (lua_key_eq(s, htap)) { + mplib_aux_push_path(L, h->htap, MPLIB_PATH, bendtolerance, movetolerance); + } else if (lua_key_eq(s, pen)) { + mplib_aux_push_path(L, h->pen, MPLIB_PEN, bendtolerance, movetolerance); + /* pushed in the table at the top */ + mplib_aux_push_pentype(L, h->pen); + } else if (lua_key_eq(s, color)) { + mplib_aux_push_color(L, (mp_graphic_object *) h); + } else if (lua_key_eq(s, linejoin)) { + lua_pushnumber(L, (lua_Number) h->linejoin); + } else if (lua_key_eq(s, linecap)) { + lua_pushnumber(L, (lua_Number) h->linecap); + // } else if (lua_key_eq(s, stacking)) { + // lua_pushinteger(L, (lua_Integer) h->stacking); + } else if (lua_key_eq(s, miterlimit)) { + lua_pushnumber(L, h->miterlimit); + } else if (lua_key_eq(s, prescript)) { + lua_pushlstring(L, h->pre_script, h->pre_length); + } else if (lua_key_eq(s, postscript)) { + lua_pushlstring(L, h->post_script, h->post_length); + } else if (lua_key_eq(s, dash)) { + mplib_aux_push_dash(L, h); + } else { + lua_pushnil(L); + } +} + +static void mplib_aux_start(lua_State *L, const char *s, struct mp_start_object *h, lua_Number bendtolerance, lua_Number movetolerance) +{ + if (lua_key_eq(s, path)) { + mplib_aux_push_path(L, h->path, MPLIB_PATH, bendtolerance, movetolerance); + } else if (lua_key_eq(s, prescript)) { + lua_pushlstring(L, h->pre_script, h->pre_length); + } else if (lua_key_eq(s, postscript)) { + lua_pushlstring(L, h->post_script, h->post_length); + // } else if (lua_key_eq(s, stacking)) { + // lua_pushinteger(L, (lua_Integer) h->stacking); + } else { + lua_pushnil(L); + } +} + +// static void mplib_aux_stop(lua_State *L, const char *s, struct mp_stop_object *h, lua_Number bendtolerance, lua_Number movetolerance) +// { +// if (lua_key_eq(s, stacking)) { +// lua_pushinteger(L, (lua_Integer) h->stacking); +// } else { +// lua_pushnil(L); +// } +// } + +static int mplib_object_index(lua_State *L) +{ + struct mp_graphic_object **hh = mplib_aux_is_gr_object(L, 1); /* no need for test */ + if (*hh) { + struct mp_graphic_object *h = *hh; + const char *s = lua_tostring(L, 2); + /* todo: remove stacking from specific aux */ + if (lua_key_eq(s, type)) { + lua_push_key_by_index(mplib_values_type[h->type]); + } else if (lua_key_eq(s, stacking)) { + lua_pushinteger(L, (lua_Integer) h->stacking); + } else { + lua_Number bendtolerance = mplib_aux_get_bend_tolerance(L, 1); + lua_Number movetolerance = mplib_aux_get_move_tolerance(L, 1); + /* todo: we can use generic casts */ + switch (h->type) { + case mp_fill_code: + case mp_stroked_code: + mplib_aux_shape(L, s, (mp_shape_object *) h, bendtolerance, movetolerance); + break; + case mp_start_clip_code: + case mp_start_group_code: + case mp_start_bounds_code: + mplib_aux_start(L, s, (mp_start_object *) h, bendtolerance, movetolerance); + break; + // case mp_stop_clip_code: + // case mp_stop_group_code: + // case mp_stop_bounds_code: + // mplib_aux_stop_clip(L, s, (mp_stop_object *) h, bendtolerance, movetolerance); + // break; + default: + lua_pushnil(L); + break; + } + } + } else { + lua_pushnil(L); + } + return 1; +} + +/* Experiment: mpx, kind, macro, arguments */ + +static int mplib_expand_tex(lua_State *L) +{ + MP mp = mplib_aux_is_mp(L, 1); + if (mp) { + int kind = lmt_tointeger(L, 2); + halfword tail = null; + halfword head = lmt_macro_to_tok(L, 3, &tail); + if (head) { + switch (kind) { + case lua_value_none_code: + case lua_value_dimension_code: + { + halfword value = 0; + halfword space = tex_get_available_token(space_token); + halfword relax = tex_get_available_token(deep_frozen_relax_token); + token_link(tail) = space; + token_link(space) = relax; + tex_begin_inserted_list(head); + lmt_error_state.intercept = 1; + lmt_error_state.last_intercept = 0; + value = tex_scan_dimen(0, 0, 0, 0, NULL); + lmt_error_state.intercept = 0; + while (cur_tok != deep_frozen_relax_token) { + tex_get_token(); + } + if (! lmt_error_state.last_intercept) { + mp_push_numeric_value(mp, (double) value * (7200.0/7227.0) / 65536.0); + break; + } else if (kind == lua_value_none_code) { + head = lmt_macro_to_tok(L, 3, &tail); + goto TRYAGAIN; + } else { + // head = lmt_macro_to_tok(L, 3, &tail); + // goto JUSTINCASE; + lua_pushboolean(L, 0); + return 1; + } + } + case lua_value_integer_code: + case lua_value_cardinal_code: + case lua_value_boolean_code: + TRYAGAIN: + { + halfword value = 0; + halfword space = tex_get_available_token(space_token); + halfword relax = tex_get_available_token(deep_frozen_relax_token); + token_link(tail) = space; + token_link(space) = relax; + tex_begin_inserted_list(head); + lmt_error_state.intercept = 1; + lmt_error_state.last_intercept = 0; + value = tex_scan_int(0, NULL); + lmt_error_state.intercept = 0; + while (cur_tok != deep_frozen_relax_token) { + tex_get_token(); + } + if (lmt_error_state.last_intercept) { + // head = lmt_macro_to_tok(L, 3, &tail); + // goto JUSTINCASE; + lua_pushboolean(L, 0); + return 1; + } else if (kind == lua_value_boolean_code) { + mp_push_boolean_value(mp, value); + break; + } else { + mp_push_numeric_value(mp, value); + break; + } + } + default: + // JUSTINCASE: + { + int len = 0; + const char *str = (const char *) lmt_get_expansion(head, &len); + mp_push_string_value(mp, str, str ? len : 0); /* len includes \0 */ + break; + } + } + } + } + lua_pushboolean(L, 1); + return 1; +} + +/* */ + +static const struct luaL_Reg mplib_instance_metatable[] = { + { "__gc", mplib_instance_collect }, + { "__tostring", mplib_instance_tostring }, + { NULL, NULL }, +}; + +static const struct luaL_Reg mplib_figure_metatable[] = { + { "__gc", mplib_figure_collect }, + { "__tostring", mplib_figure_tostring }, + { "objects", mplib_figure_objects }, + { "boundingbox", mplib_figure_bounds }, + { "width", mplib_figure_width }, + { "height", mplib_figure_height }, + { "depth", mplib_figure_depth }, + { "italic", mplib_figure_italic }, + { "charcode", mplib_figure_charcode }, + { "tolerance", mplib_figure_tolerance }, + { "stacking", mplib_figure_stacking }, + { NULL, NULL }, +}; + +static const struct luaL_Reg mplib_object_metatable[] = { + { "__gc", mplib_object_collect }, + { "__tostring", mplib_object_tostring }, + { "__index", mplib_object_index }, + { NULL, NULL }, +}; + +static const struct luaL_Reg mplib_instance_functions_list[] = { + { "execute", mplib_execute }, + { "finish", mplib_finish }, + { "getstatistics", mplib_getstatistics }, + { "getstatus", mplib_getstatus }, + { "solvepath", mplib_solvepath }, + { NULL, NULL }, +}; + +static const struct luaL_Reg mplib_functions_list[] = { + { "new", mplib_new }, + { "version", mplib_version }, + /* */ + { "getfields", mplib_getfields }, + { "gettype", mplib_gettype }, + { "gettypes", mplib_gettypes }, + { "getcolormodels", mplib_getcolormodels }, + { "getcodes", mplib_getcodes }, + { "getstates", mplib_getstates }, + { "getobjecttypes", mplib_getobjecttypes }, + { "getcallbackstate", mplib_getcallbackstate }, + /* */ + { "settolerance", mplib_set_tolerance }, + { "gettolerance", mplib_get_tolerance }, + /* indirect */ + { "execute", mplib_execute }, + { "finish", mplib_finish }, + { "showcontext", mplib_showcontext }, + { "gethashentries", mplib_gethashentries }, + { "gethashentry", mplib_gethashentry }, + { "getstatistics", mplib_getstatistics }, + { "getstatus", mplib_getstatus }, + { "solvepath", mplib_solvepath }, + /* helpers */ + { "peninfo", mplib_object_peninfo }, + /* scanners */ + { "scannext", mplib_scan_next }, + { "scanexpression", mplib_scan_expression }, + { "scantoken", mplib_scan_token }, + { "scansymbol", mplib_scan_symbol }, + { "scanproperty", mplib_scan_property }, + { "scannumeric", mplib_scan_numeric }, + { "scannumber", mplib_scan_numeric }, /* bonus */ + { "scaninteger", mplib_scan_integer }, + { "scanboolean", mplib_scan_boolean }, + { "scanstring", mplib_scan_string }, + { "scanpair", mplib_scan_pair }, + { "scancolor", mplib_scan_color }, + { "scancmykcolor", mplib_scan_cmykcolor }, + { "scantransform", mplib_scan_transform }, + { "scanpath", mplib_scan_path }, + { "scanpen", mplib_scan_pen }, + /* skippers */ + { "skiptoken", mplib_skip_token }, + /* injectors */ + { "injectnumeric", mplib_inject_numeric }, + { "injectnumber", mplib_inject_numeric }, /* bonus */ + { "injectinteger", mplib_inject_integer }, + { "injectboolean", mplib_inject_boolean }, + { "injectstring", mplib_inject_string }, + { "injectpair", mplib_inject_pair }, + { "injectcolor", mplib_inject_color }, + { "injectcmykcolor", mplib_inject_cmykcolor }, + { "injecttransform", mplib_inject_transform }, + { "injectpath", mplib_inject_path }, + { "injectwhatever", mplib_inject_whatever }, + /* */ + { "expandtex", mplib_expand_tex }, + /* */ + { NULL, NULL }, +}; + +int luaopen_mplib(lua_State *L) +{ + mplib_aux_initialize_lua(L); + + luaL_newmetatable(L, MP_METATABLE_OBJECT); + lua_pushvalue(L, -1); + lua_setfield(L, -2, "__index"); + luaL_setfuncs(L, mplib_object_metatable, 0); + luaL_newmetatable(L, MP_METATABLE_FIGURE); + lua_pushvalue(L, -1); + lua_setfield(L, -2, "__index"); + luaL_setfuncs(L, mplib_figure_metatable, 0); + luaL_newmetatable(L, MP_METATABLE_INSTANCE); + lua_pushvalue(L, -1); + lua_setfield(L, -2, "__index"); + luaL_setfuncs(L, mplib_instance_metatable, 0); + luaL_setfuncs(L, mplib_instance_functions_list, 0); + lua_newtable(L); + luaL_setfuncs(L, mplib_functions_list, 0); + return 1; +} |