diff options
Diffstat (limited to 'src/vim9compile.c')
-rw-r--r-- | src/vim9compile.c | 7655 |
1 files changed, 79 insertions, 7576 deletions
diff --git a/src/vim9compile.c b/src/vim9compile.c index 4bce9972d2..50fe7e257b 100644 --- a/src/vim9compile.c +++ b/src/vim9compile.c @@ -8,7 +8,7 @@ */ /* - * vim9compile.c: :def and dealing with instructions + * vim9compile.c: compiling a :def function */ #define USING_FLOAT_STUFF @@ -16,194 +16,15 @@ #if defined(FEAT_EVAL) || defined(PROTO) -#ifdef VMS -# include <float.h> +// When not generating protos this is included in proto.h +#ifdef PROTO +# include "vim9.h" #endif -#define DEFINE_VIM9_GLOBALS -#include "vim9.h" - -// values for ctx_skip -typedef enum { - SKIP_NOT, // condition is a constant, produce code - SKIP_YES, // condition is a constant, do NOT produce code - SKIP_UNKNOWN // condition is not a constant, produce code -} skip_T; - -/* - * Chain of jump instructions where the end label needs to be set. - */ -typedef struct endlabel_S endlabel_T; -struct endlabel_S { - endlabel_T *el_next; // chain end_label locations - int el_end_label; // instruction idx where to set end -}; - -/* - * info specific for the scope of :if / elseif / else - */ -typedef struct { - int is_seen_else; - int is_seen_skip_not; // a block was unconditionally executed - int is_had_return; // every block ends in :return - int is_if_label; // instruction idx at IF or ELSEIF - endlabel_T *is_end_label; // instructions to set end label -} ifscope_T; - -/* - * info specific for the scope of :while - */ -typedef struct { - int ws_top_label; // instruction idx at WHILE - endlabel_T *ws_end_label; // instructions to set end -} whilescope_T; - -/* - * info specific for the scope of :for - */ -typedef struct { - int fs_top_label; // instruction idx at FOR - endlabel_T *fs_end_label; // break instructions -} forscope_T; - -/* - * info specific for the scope of :try - */ -typedef struct { - int ts_try_label; // instruction idx at TRY - endlabel_T *ts_end_label; // jump to :finally or :endtry - int ts_catch_label; // instruction idx of last CATCH - int ts_caught_all; // "catch" without argument encountered -} tryscope_T; - -typedef enum { - NO_SCOPE, - IF_SCOPE, - WHILE_SCOPE, - FOR_SCOPE, - TRY_SCOPE, - BLOCK_SCOPE -} scopetype_T; - -/* - * Info for one scope, pointed to by "ctx_scope". - */ -typedef struct scope_S scope_T; -struct scope_S { - scope_T *se_outer; // scope containing this one - scopetype_T se_type; - int se_local_count; // ctx_locals.ga_len before scope - skip_T se_skip_save; // ctx_skip before the block - union { - ifscope_T se_if; - whilescope_T se_while; - forscope_T se_for; - tryscope_T se_try; - } se_u; -}; - -/* - * Entry for "ctx_locals". Used for arguments and local variables. - */ -typedef struct { - char_u *lv_name; - type_T *lv_type; - int lv_idx; // index of the variable on the stack - int lv_from_outer; // nesting level, using ctx_outer scope - int lv_const; // when TRUE cannot be assigned to - int lv_arg; // when TRUE this is an argument -} lvar_T; - -// Destination for an assignment or ":unlet" with an index. -typedef enum { - dest_local, - dest_option, - dest_func_option, - dest_env, - dest_global, - dest_buffer, - dest_window, - dest_tab, - dest_vimvar, - dest_script, - dest_reg, - dest_expr, -} assign_dest_T; - -// Used by compile_lhs() to store information about the LHS of an assignment -// and one argument of ":unlet" with an index. -typedef struct { - assign_dest_T lhs_dest; // type of destination - - char_u *lhs_name; // allocated name excluding the last - // "[expr]" or ".name". - size_t lhs_varlen; // length of the variable without - // "[expr]" or ".name" - char_u *lhs_whole; // allocated name including the last - // "[expr]" or ".name" for :redir - size_t lhs_varlen_total; // length of the variable including - // any "[expr]" or ".name" - char_u *lhs_dest_end; // end of the destination, including - // "[expr]" or ".name". - char_u *lhs_end; // end including any type - - int lhs_has_index; // has "[expr]" or ".name" - - int lhs_new_local; // create new local variable - int lhs_opt_flags; // for when destination is an option - int lhs_vimvaridx; // for when destination is a v:var - - lvar_T lhs_local_lvar; // used for existing local destination - lvar_T lhs_arg_lvar; // used for argument destination - lvar_T *lhs_lvar; // points to destination lvar - int lhs_scriptvar_sid; - int lhs_scriptvar_idx; - - int lhs_has_type; // type was specified - type_T *lhs_type; - type_T *lhs_member_type; - - int lhs_append; // used by ISN_REDIREND -} lhs_T; - -/* - * Context for compiling lines of Vim script. - * Stores info about the local variables and condition stack. - */ -struct cctx_S { - ufunc_T *ctx_ufunc; // current function - int ctx_lnum; // line number in current function - char_u *ctx_line_start; // start of current line or NULL - garray_T ctx_instr; // generated instructions - - int ctx_prev_lnum; // line number below previous command, for - // debugging - - compiletype_T ctx_compile_type; - - garray_T ctx_locals; // currently visible local variables - - int ctx_has_closure; // set to one if a closures was created in - // the function - - garray_T ctx_imports; // imported items - - skip_T ctx_skip; - scope_T *ctx_scope; // current scope, NULL at toplevel - int ctx_had_return; // last seen statement was "return" - - cctx_T *ctx_outer; // outer scope for lambda or nested - // function - int ctx_outer_used; // var in ctx_outer was used - - garray_T ctx_type_stack; // type of each item on the stack - garray_T *ctx_type_list; // list of pointers to allocated types - - int ctx_has_cmdmod; // ISN_CMDMOD was generated - - lhs_T ctx_redir_lhs; // LHS for ":redir => var", valid when - // lhs_name is not NULL -}; +// Functions defined with :def are stored in this growarray. +// They are never removed, so that they can be found by index. +// Deleted functions have the df_deleted flag set. +garray_T def_functions = {0, 0, sizeof(dfunc_T), 50, NULL}; static void delete_def_function_contents(dfunc_T *dfunc, int mark_deleted); @@ -213,7 +34,7 @@ static void delete_def_function_contents(dfunc_T *dfunc, int mark_deleted); * If "lvar" is NULL only check if the variable can be found. * Return FAIL if not found. */ - static int + int lookup_local(char_u *name, size_t len, lvar_T *lvar, cctx_T *cctx) { int idx; @@ -262,7 +83,7 @@ lookup_local(char_u *name, size_t len, lvar_T *lvar, cctx_T *cctx) * Sets "gen_load_outer" to TRUE if found in outer scope. * Returns OK when found, FAIL otherwise. */ - static int + int arg_exists( char_u *name, size_t len, @@ -397,7 +218,7 @@ find_script_var(char_u *name, size_t len, cctx_T *cctx) /* * Return TRUE if the script context is Vim9 script. */ - static int + int script_is_vim9() { return SCRIPT_ITEM(current_sctx.sc_sid)->sn_version == SCRIPT_VERSION_VIM9; @@ -408,7 +229,7 @@ script_is_vim9() * "cctx" is NULL at the script level. * Returns OK or FAIL. */ - static int + int script_var_exists(char_u *name, size_t len, cctx_T *cctx) { if (current_sctx.sc_sid <= 0) @@ -533,493 +354,6 @@ check_defined(char_u *p, size_t len, cctx_T *cctx, int is_arg) } -///////////////////////////////////////////////////////////////////// -// Following generate_ functions expect the caller to call ga_grow(). - -#define RETURN_NULL_IF_SKIP(cctx) if (cctx->ctx_skip == SKIP_YES) return NULL -#define RETURN_OK_IF_SKIP(cctx) if (cctx->ctx_skip == SKIP_YES) return OK - -/* - * Generate an instruction without arguments. - * Returns a pointer to the new instruction, NULL if failed. - */ - static isn_T * -generate_instr(cctx_T *cctx, isntype_T isn_type) -{ - garray_T *instr = &cctx->ctx_instr; - isn_T *isn; - - RETURN_NULL_IF_SKIP(cctx); - if (GA_GROW_FAILS(instr, 1)) - return NULL; - isn = ((isn_T *)instr->ga_data) + instr->ga_len; - isn->isn_type = isn_type; - isn->isn_lnum = cctx->ctx_lnum + 1; - ++instr->ga_len; - - return isn; -} - -/* - * Generate an instruction without arguments. - * "drop" will be removed from the stack. - * Returns a pointer to the new instruction, NULL if failed. - */ - static isn_T * -generate_instr_drop(cctx_T *cctx, isntype_T isn_type, int drop) -{ - garray_T *stack = &cctx->ctx_type_stack; - - RETURN_NULL_IF_SKIP(cctx); - stack->ga_len -= drop; - return generate_instr(cctx, isn_type); -} - -/* - * Generate instruction "isn_type" and put "type" on the type stack. - */ - static isn_T * -generate_instr_type(cctx_T *cctx, isntype_T isn_type, type_T *type) -{ - isn_T *isn; - garray_T *stack = &cctx->ctx_type_stack; - - if ((isn = generate_instr(cctx, isn_type)) == NULL) - return NULL; - - if (GA_GROW_FAILS(stack, 1)) - return NULL; - ((type_T **)stack->ga_data)[stack->ga_len] = type == NULL ? &t_any : type; - ++stack->ga_len; - - return isn; -} - -/* - * Generate an ISN_DEBUG instruction. - */ - static isn_T * -generate_instr_debug(cctx_T *cctx) -{ - isn_T *isn; - dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data) - + cctx->ctx_ufunc->uf_dfunc_idx; - - if ((isn = generate_instr(cctx, ISN_DEBUG)) == NULL) - return NULL; - isn->isn_arg.debug.dbg_var_names_len = dfunc->df_var_names.ga_len; - isn->isn_arg.debug.dbg_break_lnum = cctx->ctx_prev_lnum; - return isn; -} - -/* - * If type at "offset" isn't already VAR_STRING then generate ISN_2STRING. - * But only for simple types. - * When "tolerant" is TRUE convert most types to string, e.g. a List. - */ - static int -may_generate_2STRING(int offset, int tolerant, cctx_T *cctx) -{ - isn_T *isn; - isntype_T isntype = ISN_2STRING; - garray_T *stack = &cctx->ctx_type_stack; - type_T **type; - - RETURN_OK_IF_SKIP(cctx); - type = ((type_T **)stack->ga_data) + stack->ga_len + offset; - switch ((*type)->tt_type) - { - // nothing to be done - case VAR_STRING: return OK; - - // conversion possible - case VAR_SPECIAL: - case VAR_BOOL: - case VAR_NUMBER: - case VAR_FLOAT: - break; - - // conversion possible (with runtime check) - case VAR_ANY: - case VAR_UNKNOWN: - isntype = ISN_2STRING_ANY; - break; - - // conversion possible when tolerant - case VAR_LIST: - if (tolerant) - { - isntype = ISN_2STRING_ANY; - break; - } - // FALLTHROUGH - - // conversion not possible - case VAR_VOID: - case VAR_BLOB: - case VAR_FUNC: - case VAR_PARTIAL: - case VAR_DICT: - case VAR_JOB: - case VAR_CHANNEL: - case VAR_INSTR: - to_string_error((*type)->tt_type); - return FAIL; - } - - *type = &t_string; - if ((isn = generate_instr(cctx, isntype)) == NULL) - return FAIL; - isn->isn_arg.tostring.offset = offset; - isn->isn_arg.tostring.tolerant = tolerant; - - return OK; -} - - static int -check_number_or_float(vartype_T type1, vartype_T type2, char_u *op) -{ - if (!((type1 == VAR_NUMBER || type1 == VAR_FLOAT || type1 == VAR_ANY) - && (type2 == VAR_NUMBER || type2 == VAR_FLOAT - || type2 == VAR_ANY))) - { - if (*op == '+') - emsg(_(e_wrong_argument_type_for_plus)); - else - semsg(_(e_char_requires_number_or_float_arguments), *op); - return FAIL; - } - return OK; -} - -/* - * Generate instruction for "+". For a list this creates a new list. - */ - static int -generate_add_instr( - cctx_T *cctx, - vartype_T vartype, - type_T *type1, - type_T *type2, - exprtype_T expr_type) -{ - garray_T *stack = &cctx->ctx_type_stack; - isn_T *isn = generate_instr_drop(cctx, - vartype == VAR_NUMBER ? ISN_OPNR - : vartype == VAR_LIST ? ISN_ADDLIST - : vartype == VAR_BLOB ? ISN_ADDBLOB -#ifdef FEAT_FLOAT - : vartype == VAR_FLOAT ? ISN_OPFLOAT -#endif - : ISN_OPANY, 1); - - if (vartype != VAR_LIST && vartype != VAR_BLOB - && type1->tt_type != VAR_ANY - && type2->tt_type != VAR_ANY - && check_number_or_float( - type1->tt_type, type2->tt_type, (char_u *)"+") == FAIL) - return FAIL; - - if (isn != NULL) - { - if (isn->isn_type == ISN_ADDLIST) - isn->isn_arg.op.op_type = expr_type; - else - isn->isn_arg.op.op_type = EXPR_ADD; - } - - // When concatenating two lists with different member types the member type - // becomes "any". - if (vartype == VAR_LIST - && type1->tt_type == VAR_LIST && type2->tt_type == VAR_LIST - && type1->tt_member != type2->tt_member) - (((type_T **)stack->ga_data)[stack->ga_len - 1]) = &t_list_any; - - return isn == NULL ? FAIL : OK; -} - -/* - * Get the type to use for an instruction for an operation on "type1" and - * "type2". If they are matching use a type-specific instruction. Otherwise - * fall back to runtime type checking. - */ - static vartype_T -operator_type(type_T *type1, type_T *type2) -{ - if (type1->tt_type == type2->tt_type - && (type1->tt_type == VAR_NUMBER - || type1->tt_type == VAR_LIST -#ifdef FEAT_FLOAT - || type1->tt_type == VAR_FLOAT -#endif - || type1->tt_type == VAR_BLOB)) - return type1->tt_type; - return VAR_ANY; -} - -/* - * Generate an instruction with two arguments. The instruction depends on the - * type of the arguments. - */ - static int -generate_two_op(cctx_T *cctx, char_u *op) -{ - garray_T *stack = &cctx->ctx_type_stack; - type_T *type1; - type_T *type2; - vartype_T vartype; - isn_T *isn; - - RETURN_OK_IF_SKIP(cctx); - - // Get the known type of the two items on the stack. - type1 = ((type_T **)stack->ga_data)[stack->ga_len - 2]; - type2 = ((type_T **)stack->ga_data)[stack->ga_len - 1]; - vartype = operator_type(type1, type2); - - switch (*op) - { - case '+': - if (generate_add_instr(cctx, vartype, type1, type2, - EXPR_COPY) == FAIL) - return FAIL; - break; - - case '-': - case '*': - case '/': if (check_number_or_float(type1->tt_type, type2->tt_type, - op) == FAIL) - return FAIL; - if (vartype == VAR_NUMBER) - isn = generate_instr_drop(cctx, ISN_OPNR, 1); -#ifdef FEAT_FLOAT - else if (vartype == VAR_FLOAT) - isn = generate_instr_drop(cctx, ISN_OPFLOAT, 1); -#endif - else - isn = generate_instr_drop(cctx, ISN_OPANY, 1); - if (isn != NULL) - isn->isn_arg.op.op_type = *op == '*' - ? EXPR_MULT : *op == '/'? EXPR_DIV : EXPR_SUB; - break; - - case '%': if ((type1->tt_type != VAR_ANY - && type1->tt_type != VAR_NUMBER) - || (type2->tt_type != VAR_ANY - && type2->tt_type != VAR_NUMBER)) - { - emsg(_(e_percent_requires_number_arguments)); - return FAIL; - } - isn = generate_instr_drop(cctx, - vartype == VAR_NUMBER ? ISN_OPNR : ISN_OPANY, 1); - if (isn != NULL) - isn->isn_arg.op.op_type = EXPR_REM; - break; - } - - // correct type of result - if (vartype == VAR_ANY) - { - type_T *type = &t_any; - -#ifdef FEAT_FLOAT - // float+number and number+float results in float - if ((type1->tt_type == VAR_NUMBER || type1->tt_type == VAR_FLOAT) - && (type2->tt_type == VAR_NUMBER || type2->tt_type == VAR_FLOAT)) - type = &t_float; -#endif - ((type_T **)stack->ga_data)[stack->ga_len - 1] = type; - } - - return OK; -} - -/* - * Get the instruction to use for comparing "type1" with "type2" - * Return ISN_DROP when failed. - */ - static isntype_T -get_compare_isn(exprtype_T exprtype, vartype_T type1, vartype_T type2) -{ - isntype_T isntype = ISN_DROP; - - if (type1 == VAR_UNKNOWN) - type1 = VAR_ANY; - if (type2 == VAR_UNKNOWN) - type2 = VAR_ANY; - - if (type1 == type2) - { - switch (type1) - { - case VAR_BOOL: isntype = ISN_COMPAREBOOL; break; - case VAR_SPECIAL: isntype = ISN_COMPARESPECIAL; break; - case VAR_NUMBER: isntype = ISN_COMPARENR; break; - case VAR_FLOAT: isntype = ISN_COMPAREFLOAT; break; - case VAR_STRING: isntype = ISN_COMPARESTRING; break; - case VAR_BLOB: isntype = ISN_COMPAREBLOB; break; - case VAR_LIST: isntype = ISN_COMPARELIST; break; - case VAR_DICT: isntype = ISN_COMPAREDICT; break; - case VAR_FUNC: isntype = ISN_COMPAREFUNC; break; - default: isntype = ISN_COMPAREANY; break; - } - } - else if (type1 == VAR_ANY || type2 == VAR_ANY - || ((type1 == VAR_NUMBER || type1 == VAR_FLOAT) - && (type2 == VAR_NUMBER || type2 == VAR_FLOAT))) - isntype = ISN_COMPAREANY; - - if ((exprtype == EXPR_IS || exprtype == EXPR_ISNOT) - && (isntype == ISN_COMPAREBOOL - || isntype == ISN_COMPARESPECIAL - || isntype == ISN_COMPARENR - || isntype == ISN_COMPAREFLOAT)) - { - semsg(_(e_cannot_use_str_with_str), - exprtype == EXPR_IS ? "is" : "isnot" , vartype_name(type1)); - return ISN_DROP; - } - if (isntype == ISN_DROP - || ((exprtype != EXPR_EQUAL && exprtype != EXPR_NEQUAL - && (type1 == VAR_BOOL || type1 == VAR_SPECIAL - || type2 == VAR_BOOL || type2 == VAR_SPECIAL))) - || ((exprtype != EXPR_EQUAL && exprtype != EXPR_NEQUAL - && exprtype != EXPR_IS && exprtype != EXPR_ISNOT - && (type1 == VAR_BLOB || type2 == VAR_BLOB - || type1 == VAR_LIST || type2 == VAR_LIST)))) - { - semsg(_(e_cannot_compare_str_with_str), - vartype_name(type1), vartype_name(type2)); - return ISN_DROP; - } - return isntype; -} - - int -check_compare_types(exprtype_T type, typval_T *tv1, typval_T *tv2) -{ - if (get_compare_isn(type, tv1->v_type, tv2->v_type) == ISN_DROP) - return FAIL; - return OK; -} - -/* - * Generate an ISN_COMPARE* instruction with a boolean result. - */ - static int -generate_COMPARE(cctx_T *cctx, exprtype_T exprtype, int ic) -{ - isntype_T isntype; - isn_T *isn; - garray_T *stack = &cctx->ctx_type_stack; - vartype_T type1; - vartype_T type2; - - RETURN_OK_IF_SKIP(cctx); - - // Get the known type of the two items on the stack. If they are matching - // use a type-specific instruction. Otherwise fall back to runtime type - // checking. - type1 = ((type_T **)stack->ga_data)[stack->ga_len - 2]->tt_type; - type2 = ((type_T **)stack->ga_data)[stack->ga_len - 1]->tt_type; - isntype = get_compare_isn(exprtype, type1, type2); - if (isntype == ISN_DROP) - return FAIL; - - if ((isn = generate_instr(cctx, isntype)) == NULL) - return FAIL; - isn->isn_arg.op.op_type = exprtype; - isn->isn_arg.op.op_ic = ic; - - // takes two arguments, puts one bool back - if (stack->ga_len >= 2) - { - --stack->ga_len; - ((type_T **)stack->ga_data)[stack->ga_len - 1] = &t_bool; - } - - return OK; -} - -/* - * Generate an ISN_2BOOL instruction. - * "offset" is the offset in the type stack. - */ - static int -generate_2BOOL(cctx_T *cctx, int invert, int offset) -{ - isn_T *isn; - garray_T *stack = &cctx->ctx_type_stack; - - RETURN_OK_IF_SKIP(cctx); - if ((isn = generate_instr(cctx, ISN_2BOOL)) == NULL) - return FAIL; - isn->isn_arg.tobool.invert = invert; - isn->isn_arg.tobool.offset = offset; - - // type becomes bool - ((type_T **)stack->ga_data)[stack->ga_len + offset] = &t_bool; - - return OK; -} - -/* - * Generate an ISN_COND2BOOL instruction. - */ - static int -generate_COND2BOOL(cctx_T *cctx) -{ - isn_T *isn; - garray_T *stack = &cctx->ctx_type_stack; - - RETURN_OK_IF_SKIP(cctx); - if ((isn = generate_instr(cctx, ISN_COND2BOOL)) == NULL) - return FAIL; - - // type becomes bool - ((type_T **)stack->ga_data)[stack->ga_len - 1] = &t_bool; - - return OK; -} - - static int -generate_TYPECHECK( - cctx_T *cctx, - type_T *expected, - int offset, - int argidx) -{ - isn_T *isn; - garray_T *stack = &cctx->ctx_type_stack; - - RETURN_OK_IF_SKIP(cctx); - if ((isn = generate_instr(cctx, ISN_CHECKTYPE)) == NULL) - return FAIL; - isn->isn_arg.type.ct_type = alloc_type(expected); - isn->isn_arg.type.ct_off = (int8_T)offset; - isn->isn_arg.type.ct_arg_idx = (int8_T)argidx; - - // type becomes expected - ((type_T **)stack->ga_data)[stack->ga_len + offset] = expected; - - return OK; -} - - static int -generate_SETTYPE( - cctx_T *cctx, - type_T *expected) -{ - isn_T *isn; - - RETURN_OK_IF_SKIP(cctx); - if ((isn = generate_instr(cctx, ISN_SETTYPE)) == NULL) - return FAIL; - isn->isn_arg.type.ct_type = alloc_type(expected); - return OK; -} - /* * Return TRUE if "actual" could be "expected" and a runtime typecheck is to be * used. Return FALSE if the types will never match. @@ -1108,1397 +442,10 @@ need_type( } /* - * Check that the top of the type stack has a type that can be used as a - * condition. Give an error and return FAIL if not. - */ - static int -bool_on_stack(cctx_T *cctx) -{ - garray_T *stack = &cctx->ctx_type_stack; - type_T *type; - - type = ((type_T **)stack->ga_data)[stack->ga_len - 1]; - if (type == &t_bool) - return OK; - - if (type == &t_any || type == &t_number || type == &t_number_bool) - // Number 0 and 1 are OK to use as a bool. "any" could also be a bool. - // This requires a runtime type check. - return generate_COND2BOOL(cctx); - - return need_type(type, &t_bool, -1, 0, cctx, FALSE, FALSE); -} - -/* - * Generate an ISN_PUSHNR instruction. - */ - static int -generate_PUSHNR(cctx_T *cctx, varnumber_T number) -{ - isn_T *isn; - garray_T *stack = &cctx->ctx_type_stack; - - RETURN_OK_IF_SKIP(cctx); - if ((isn = generate_instr_type(cctx, ISN_PUSHNR, &t_number)) == NULL) - return FAIL; - isn->isn_arg.number = number; - - if (number == 0 || number == 1) - // A 0 or 1 number can also be used as a bool. - ((type_T **)stack->ga_data)[stack->ga_len - 1] = &t_number_bool; - return OK; -} - -/* - * Generate an ISN_PUSHBOOL instruction. - */ - static int -generate_PUSHBOOL(cctx_T *cctx, varnumber_T number) -{ - isn_T *isn; - - RETURN_OK_IF_SKIP(cctx); - if ((isn = generate_instr_type(cctx, ISN_PUSHBOOL, &t_bool)) == NULL) - return FAIL; - isn->isn_arg.number = number; - - return OK; -} - -/* - * Generate an ISN_PUSHSPEC instruction. - */ - static int -generate_PUSHSPEC(cctx_T *cctx, varnumber_T number) -{ - isn_T *isn; - - RETURN_OK_IF_SKIP(cctx); - if ((isn = generate_instr_type(cctx, ISN_PUSHSPEC, &t_special)) == NULL) - return FAIL; - isn->isn_arg.number = number; - - return OK; -} - -#ifdef FEAT_FLOAT -/* - * Generate an ISN_PUSHF instruction. - */ - static int -generate_PUSHF(cctx_T *cctx, float_T fnumber) -{ - isn_T *isn; - - RETURN_OK_IF_SKIP(cctx); - if ((isn = generate_instr_type(cctx, ISN_PUSHF, &t_float)) == NULL) - return FAIL; - isn->isn_arg.fnumber = fnumber; - - return OK; -} -#endif - -/* - * Generate an ISN_PUSHS instruction. - * Consumes "*str". When freed *str is set to NULL, unless "str" is NULL. - */ - static int -generate_PUSHS(cctx_T *cctx, char_u **str) -{ - isn_T *isn; - - if (cctx->ctx_skip == SKIP_YES) - { - if (str != NULL) - VIM_CLEAR(*str); - return OK; - } - if ((isn = generate_instr_type(cctx, ISN_PUSHS, &t_string)) == NULL) - { - if (str != NULL) - VIM_CLEAR(*str); - return FAIL; - } - isn->isn_arg.string = str == NULL ? NULL : *str; - - return OK; -} - -/* - * Generate an ISN_PUSHCHANNEL instruction. - * Consumes "channel". - */ - static int -generate_PUSHCHANNEL(cctx_T *cctx, channel_T *channel) -{ - isn_T *isn; - - RETURN_OK_IF_SKIP(cctx); - if ((isn = generate_instr_type(cctx, ISN_PUSHCHANNEL, &t_channel)) == NULL) - return FAIL; - isn->isn_arg.channel = channel; - - return OK; -} - -/* - * Generate an ISN_PUSHJOB instruction. - * Consumes "job". - */ - static int -generate_PUSHJOB(cctx_T *cctx, job_T *job) -{ - isn_T *isn; - - RETURN_OK_IF_SKIP(cctx); - if ((isn = generate_instr_type(cctx, ISN_PUSHJOB, &t_channel)) == NULL) - return FAIL; - isn->isn_arg.job = job; - - return OK; -} - -/* - * Generate an ISN_PUSHBLOB instruction. - * Consumes "blob". - */ - static int -generate_PUSHBLOB(cctx_T *cctx, blob_T *blob) -{ - isn_T *isn; - - RETURN_OK_IF_SKIP(cctx); - if ((isn = generate_instr_type(cctx, ISN_PUSHBLOB, &t_blob)) == NULL) - return FAIL; - isn->isn_arg.blob = blob; - - return OK; -} - -/* - * Generate an ISN_PUSHFUNC instruction with name "name". - * Consumes "name". - */ - static int -generate_PUSHFUNC(cctx_T *cctx, char_u *name, type_T *type) -{ - isn_T *isn; - char_u *funcname; - - RETURN_OK_IF_SKIP(cctx); - if ((isn = generate_instr_type(cctx, ISN_PUSHFUNC, type)) == NULL) - return FAIL; - if (name == NULL) - funcname = NULL; - else if (*name == K_SPECIAL) // script-local - funcname = vim_strsave(name); - else - { - funcname = alloc(STRLEN(name) + 3); - if (funcname != NULL) - { - STRCPY(funcname, "g:"); - STRCPY(funcname + 2, name); - } - } - - isn->isn_arg.string = funcname; - return OK; -} - -/* - * Generate an ISN_GETITEM instruction with "index". - * "with_op" is TRUE for "+=" and other operators, the stack has the current - * value below the list with values. - */ - static int -generate_GETITEM(cctx_T *cctx, int index, int with_op) -{ - isn_T *isn; - garray_T *stack = &cctx->ctx_type_stack; - type_T *type = ((type_T **)stack->ga_data)[stack->ga_len - - (with_op ? 2 : 1)]; - type_T *item_type = &t_any; - - RETURN_OK_IF_SKIP(cctx); - - if (type->tt_type != VAR_LIST) - { - // cannot happen, caller has checked the type - emsg(_(e_listreq)); - return FAIL; - } - item_type = type->tt_member; - if ((isn = generate_instr(cctx, ISN_GETITEM)) == NULL) - return FAIL; - isn->isn_arg.getitem.gi_index = index; - isn->isn_arg.getitem.gi_with_op = with_op; - - // add the item type to the type stack - if (GA_GROW_FAILS(stack, 1)) - return FAIL; - ((type_T **)stack->ga_data)[stack->ga_len] = item_type; - ++stack->ga_len; - return OK; -} - -/* - * Generate an ISN_SLICE instruction with "count". - */ - static int -generate_SLICE(cctx_T *cctx, int count) -{ - isn_T *isn; - - RETURN_OK_IF_SKIP(cctx); - if ((isn = generate_instr(cctx, ISN_SLICE)) == NULL) - return FAIL; - isn->isn_arg.number = count; - return OK; -} - -/* - * Generate an ISN_CHECKLEN instruction with "min_len". - */ - static int -generate_CHECKLEN(cctx_T *cctx, int min_len, int more_OK) -{ - isn_T *isn; - - RETURN_OK_IF_SKIP(cctx); - - if ((isn = generate_instr(cctx, ISN_CHECKLEN)) == NULL) - return FAIL; - isn->isn_arg.checklen.cl_min_len = min_len; - isn->isn_arg.checklen.cl_more_OK = more_OK; - - return OK; -} - -/* - * Generate an ISN_STORE instruction. - */ - static int -generate_STORE(cctx_T *cctx, isntype_T isn_type, int idx, char_u *name) -{ - isn_T *isn; - - RETURN_OK_IF_SKIP(cctx); - if ((isn = generate_instr_drop(cctx, isn_type, 1)) == NULL) - return FAIL; - if (name != NULL) - isn->isn_arg.string = vim_strsave(name); - else - isn->isn_arg.number = idx; - - return OK; -} - -/* - * Generate an ISN_STOREOUTER instruction. - */ - static int -generate_STOREOUTER(cctx_T *cctx, int idx, int level) -{ - isn_T *isn; - - RETURN_OK_IF_SKIP(cctx); - if ((isn = generate_instr_drop(cctx, ISN_STOREOUTER, 1)) == NULL) - return FAIL; - isn->isn_arg.outer.outer_idx = idx; - isn->isn_arg.outer.outer_depth = level; - - return OK; -} - -/* - * Generate an ISN_STORENR instruction (short for ISN_PUSHNR + ISN_STORE) - */ - static int -generate_STORENR(cctx_T *cctx, int idx, varnumber_T value) -{ - isn_T *isn; - - RETURN_OK_IF_SKIP(cctx); - if ((isn = generate_instr(cctx, ISN_STORENR)) == NULL) - return FAIL; - isn->isn_arg.storenr.stnr_idx = idx; - isn->isn_arg.storenr.stnr_val = value; - - return OK; -} - -/* - * Generate an ISN_STOREOPT or ISN_STOREFUNCOPT instruction - */ - static int -generate_STOREOPT( - cctx_T *cctx, - isntype_T isn_type, - char_u *name, - int opt_flags) -{ - isn_T *isn; - - RETURN_OK_IF_SKIP(cctx); - if ((isn = generate_instr_drop(cctx, isn_type, 1)) == NULL) - return FAIL; - isn->isn_arg.storeopt.so_name = vim_strsave(name); - isn->isn_arg.storeopt.so_flags = opt_flags; - - return OK; -} - -/* - * Generate an ISN_LOAD or similar instruction. - */ - static int -generate_LOAD( - cctx_T *cctx, - isntype_T isn_type, - int idx, - char_u *name, - type_T *type) -{ - isn_T *isn; - - RETURN_OK_IF_SKIP(cctx); - if ((isn = generate_instr_type(cctx, isn_type, type)) == NULL) - return FAIL; - if (name != NULL) - isn->isn_arg.string = vim_strsave(name); - else - isn->isn_arg.number = idx; - - return OK; -} - -/* - * Generate an ISN_LOADOUTER instruction - */ - static int -generate_LOADOUTER( - cctx_T *cctx, - int idx, - int nesting, - type_T *type) -{ - isn_T *isn; - - RETURN_OK_IF_SKIP(cctx); - if ((isn = generate_instr_type(cctx, ISN_LOADOUTER, type)) == NULL) - return FAIL; - isn->isn_arg.outer.outer_idx = idx; - isn->isn_arg.outer.outer_depth = nesting; - - return OK; -} - -/* - * Generate an ISN_LOADV instruction for v:var. - */ - static int -generate_LOADV( - cctx_T *cctx, - char_u *name, - int error) -{ - int di_flags; - int vidx = find_vim_var(name, &di_flags); - type_T *type; - - RETURN_OK_IF_SKIP(cctx); - if (vidx < 0) - { - if (error) - semsg(_(e_variable_not_found_str), name); - return FAIL; - } - type = typval2type_vimvar(get_vim_var_tv(vidx), cctx->ctx_type_list); - - return generate_LOAD(cctx, ISN_LOADV, vidx, NULL, type); -} - -/* - * Generate an ISN_UNLET instruction. - */ - static int -generate_UNLET(cctx_T *cctx, isntype_T isn_type, char_u *name, int forceit) -{ - isn_T *isn; - - RETURN_OK_IF_SKIP(cctx); - if ((isn = generate_instr(cctx, isn_type)) == NULL) - return FAIL; - isn->isn_arg.unlet.ul_name = vim_strsave(name); - isn->isn_arg.unlet.ul_forceit = forceit; - - return OK; -} - -/* - * Generate an ISN_LOCKCONST instruction. - */ - static int -generate_LOCKCONST(cctx_T *cctx) -{ - isn_T *isn; - - RETURN_OK_IF_SKIP(cctx); - if ((isn = generate_instr(cctx, ISN_LOCKCONST)) == NULL) - return FAIL; - return OK; -} - -/* - * Generate an ISN_LOADS instruction. - */ - static int -generate_OLDSCRIPT( - cctx_T *cctx, - isntype_T isn_type, - char_u *name, - int sid, - type_T *type) -{ - isn_T *isn; - - RETURN_OK_IF_SKIP(cctx); - if (isn_type == ISN_LOADS) - isn = generate_instr_type(cctx, isn_type, type); - else - isn = generate_instr_drop(cctx, isn_type, 1); - if (isn == NULL) - return FAIL; - isn->isn_arg.loadstore.ls_name = vim_strsave(name); - isn->isn_arg.loadstore.ls_sid = sid; - - return OK; -} - -/* - * Generate an ISN_LOADSCRIPT or ISN_STORESCRIPT instruction. - */ - static int -generate_VIM9SCRIPT( - cctx_T *cctx, - isntype_T isn_type, - int sid, - int idx, - type_T *type) -{ - isn_T *isn; - scriptref_T *sref; - scriptitem_T *si = SCRIPT_ITEM(sid); - - RETURN_OK_IF_SKIP(cctx); - if (isn_type == ISN_LOADSCRIPT) - isn = generate_instr_type(cctx, isn_type, type); - else - isn = generate_instr_drop(cctx, isn_type, 1); - if (isn == NULL) - return FAIL; - - // This requires three arguments, which doesn't fit in an instruction, thus - // we need to allocate a struct for this. - sref = ALLOC_ONE(scriptref_T); - if (sref == NULL) - return FAIL; - isn->isn_arg.script.scriptref = sref; - sref->sref_sid = sid; - sref->sref_idx = idx; - sref->sref_seq = si->sn_script_seq; - sref->sref_type = type; - return OK; |