summaryrefslogtreecommitdiffstats
path: root/src/vim9compile.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/vim9compile.c')
-rw-r--r--src/vim9compile.c7655
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;