mirror of
https://github.com/vim/vim.git
synced 2026-05-08 13:19:47 -04:00
patch 9.1.1232: Vim script is missing the tuple data type
Problem: Vim script is missing the tuple data type
Solution: Add support for the tuple data type
(Yegappan Lakshmanan)
closes: #16776
Signed-off-by: Yegappan Lakshmanan <yegappan@yahoo.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
This commit is contained in:
committed by
Christian Brabandt
parent
adb703e1b9
commit
9cb865e95b
@@ -157,6 +157,7 @@ SRC_ALL = \
|
||||
src/textobject.c \
|
||||
src/textprop.c \
|
||||
src/time.c \
|
||||
src/tuple.c \
|
||||
src/typval.c \
|
||||
src/ui.c \
|
||||
src/undo.c \
|
||||
@@ -339,6 +340,7 @@ SRC_ALL = \
|
||||
src/proto/textobject.pro \
|
||||
src/proto/textprop.pro \
|
||||
src/proto/time.pro \
|
||||
src/proto/tuple.pro \
|
||||
src/proto/typval.pro \
|
||||
src/proto/ui.pro \
|
||||
src/proto/undo.pro \
|
||||
|
||||
+134
-71
@@ -1,4 +1,4 @@
|
||||
*builtin.txt* For Vim version 9.1. Last change: 2025 Mar 22
|
||||
*builtin.txt* For Vim version 9.1. Last change: 2025 Mar 23
|
||||
|
||||
|
||||
VIM REFERENCE MANUAL by Bram Moolenaar
|
||||
@@ -207,7 +207,7 @@ foldclosedend({lnum}) Number last line of fold at {lnum} if closed
|
||||
foldlevel({lnum}) Number fold level at {lnum}
|
||||
foldtext() String line displayed for closed fold
|
||||
foldtextresult({lnum}) String text for closed fold at {lnum}
|
||||
foreach({expr1}, {expr2}) List/Dict/Blob/String
|
||||
foreach({expr1}, {expr2}) List/Tuple/Dict/Blob/String
|
||||
for each item in {expr1} call {expr2}
|
||||
foreground() Number bring the Vim window to the foreground
|
||||
fullcommand({name} [, {vim9}]) String get full command from {name}
|
||||
@@ -348,7 +348,7 @@ job_start({command} [, {options}])
|
||||
Job start a job
|
||||
job_status({job}) String get the status of {job}
|
||||
job_stop({job} [, {how}]) Number stop {job}
|
||||
join({list} [, {sep}]) String join {list} items into one String
|
||||
join({expr} [, {sep}]) String join items in {expr} into one String
|
||||
js_decode({string}) any decode JS style JSON
|
||||
js_encode({expr}) String encode JS style JSON
|
||||
json_decode({string}) any decode JSON
|
||||
@@ -364,6 +364,7 @@ line2byte({lnum}) Number byte count of line {lnum}
|
||||
lispindent({lnum}) Number Lisp indent for line {lnum}
|
||||
list2blob({list}) Blob turn {list} of numbers into a Blob
|
||||
list2str({list} [, {utf8}]) String turn {list} of numbers into a String
|
||||
list2tuple({list}) Tuple turn {list} of items into a tuple
|
||||
listener_add({callback} [, {buf}])
|
||||
Number add a callback to listen to changes
|
||||
listener_flush([{buf}]) none invoke listener callbacks
|
||||
@@ -511,10 +512,10 @@ remove({blob}, {idx} [, {end}]) Number/Blob
|
||||
remove bytes {idx}-{end} from {blob}
|
||||
remove({dict}, {key}) any remove entry {key} from {dict}
|
||||
rename({from}, {to}) Number rename (move) file from {from} to {to}
|
||||
repeat({expr}, {count}) List/Blob/String
|
||||
repeat({expr}, {count}) List/Tuple/Blob/String
|
||||
repeat {expr} {count} times
|
||||
resolve({filename}) String get filename a shortcut points to
|
||||
reverse({obj}) List/Blob/String
|
||||
reverse({obj}) List/Tuple/Blob/String
|
||||
reverse {obj}
|
||||
round({expr}) Float round off {expr}
|
||||
rubyeval({expr}) any evaluate |Ruby| expression
|
||||
@@ -713,6 +714,7 @@ test_null_job() Job null value for testing
|
||||
test_null_list() List null value for testing
|
||||
test_null_partial() Funcref null value for testing
|
||||
test_null_string() String null value for testing
|
||||
test_null_tuple() Tuple null value for testing
|
||||
test_option_not_set({name}) none reset flag indicating option was set
|
||||
test_override({expr}, {val}) none test with Vim internal overrides
|
||||
test_refcount({expr}) Number get the reference count of {expr}
|
||||
@@ -734,6 +736,7 @@ tr({src}, {fromstr}, {tostr}) String translate chars of {src} in {fromstr}
|
||||
trim({text} [, {mask} [, {dir}]])
|
||||
String trim characters in {mask} from {text}
|
||||
trunc({expr}) Float truncate Float {expr}
|
||||
tuple2list({tuple}) List turn {tuple} of items into a list
|
||||
type({expr}) Number type of value {expr}
|
||||
typename({expr}) String representation of the type of {expr}
|
||||
undofile({name}) String undo file name for {name}
|
||||
@@ -2073,7 +2076,8 @@ copy({expr}) *copy()*
|
||||
that the original |List| can be changed without changing the
|
||||
copy, and vice versa. But the items are identical, thus
|
||||
changing an item changes the contents of both |Lists|.
|
||||
A |Dictionary| is copied in a similar way as a |List|.
|
||||
A |Tuple| or |Dictionary| is copied in a similar way as a
|
||||
|List|.
|
||||
Also see |deepcopy()|.
|
||||
Can also be used as a |method|: >
|
||||
mylist->copy()
|
||||
@@ -2116,10 +2120,10 @@ cosh({expr}) *cosh()*
|
||||
|
||||
count({comp}, {expr} [, {ic} [, {start}]]) *count()* *E706*
|
||||
Return the number of times an item with value {expr} appears
|
||||
in |String|, |List| or |Dictionary| {comp}.
|
||||
in |String|, |List|, |Tuple| or |Dictionary| {comp}.
|
||||
|
||||
If {start} is given then start with the item with this index.
|
||||
{start} can only be used with a |List|.
|
||||
{start} can only be used with a |List| or a |Tuple|.
|
||||
|
||||
When {ic} is given and it's |TRUE| then case is ignored.
|
||||
|
||||
@@ -2239,7 +2243,8 @@ deepcopy({expr} [, {noref}]) *deepcopy()* *E698*
|
||||
|Dictionary|, a copy for it is made, recursively. Thus
|
||||
changing an item in the copy does not change the contents of
|
||||
the original |List|.
|
||||
A |Dictionary| is copied in a similar way as a |List|.
|
||||
A |Tuple| or |Dictionary| is copied in a similar way as a
|
||||
|List|.
|
||||
|
||||
When {noref} is omitted or zero a contained |List| or
|
||||
|Dictionary| is only copied once. All references point to
|
||||
@@ -2547,8 +2552,8 @@ echoraw({string}) *echoraw()*
|
||||
|
||||
empty({expr}) *empty()*
|
||||
Return the Number 1 if {expr} is empty, zero otherwise.
|
||||
- A |List| or |Dictionary| is empty when it does not have any
|
||||
items.
|
||||
- A |List|, |Tuple| or |Dictionary| is empty when it does
|
||||
not have any items.
|
||||
- A |String| is empty when its length is zero.
|
||||
- A |Number| and |Float| are empty when their value is zero.
|
||||
- |v:false|, |v:none| and |v:null| are empty, |v:true| is not.
|
||||
@@ -3475,8 +3480,9 @@ foldtextresult({lnum}) *foldtextresult()*
|
||||
Return type: |String|
|
||||
|
||||
|
||||
foreach({expr1}, {expr2}) *foreach()*
|
||||
{expr1} must be a |List|, |String|, |Blob| or |Dictionary|.
|
||||
foreach({expr1}, {expr2}) *foreach()* *E1525*
|
||||
{expr1} must be a |List|, |Tuple|, |String|, |Blob| or
|
||||
|Dictionary|.
|
||||
For each item in {expr1} execute {expr2}. {expr1} is not
|
||||
modified; its values may be, as with |:lockvar| 1. |E741|
|
||||
See |map()| and |filter()| to modify {expr1}.
|
||||
@@ -3485,10 +3491,10 @@ foreach({expr1}, {expr2}) *foreach()*
|
||||
|
||||
If {expr2} is a |string|, inside {expr2} |v:val| has the value
|
||||
of the current item. For a |Dictionary| |v:key| has the key
|
||||
of the current item and for a |List| |v:key| has the index of
|
||||
the current item. For a |Blob| |v:key| has the index of the
|
||||
current byte. For a |String| |v:key| has the index of the
|
||||
current character.
|
||||
of the current item and for a |List| or a |Tuple| |v:key| has
|
||||
the index of the current item. For a |Blob| |v:key| has the
|
||||
index of the current byte. For a |String| |v:key| has the
|
||||
index of the current character.
|
||||
Examples: >
|
||||
call foreach(mylist, 'used[v:val] = true')
|
||||
< This records the items that are in the {expr1} list.
|
||||
@@ -3514,8 +3520,8 @@ foreach({expr1}, {expr2}) *foreach()*
|
||||
Can also be used as a |method|: >
|
||||
mylist->foreach(expr2)
|
||||
<
|
||||
Return type: |String|, |Blob| list<{type}> or dict<{type}>
|
||||
depending on {expr1}
|
||||
Return type: |String|, |Blob|, list<{type}>, tuple<{type}> or
|
||||
dict<{type}> depending on {expr1}
|
||||
|
||||
*foreground()*
|
||||
foreground() Move the Vim window to the foreground. Useful when sent from
|
||||
@@ -3688,6 +3694,15 @@ get({list}, {idx} [, {default}]) *get()* *get()-list*
|
||||
<
|
||||
Return type: any, depending on {list}
|
||||
|
||||
get({tuple}, {idx} [, {default}]) *get()-tuple*
|
||||
Get item {idx} from |Tuple| {tuple}. When this item is not
|
||||
available return {default}. Return zero when {default} is
|
||||
omitted.
|
||||
Preferably used as a |method|: >
|
||||
mytuple->get(idx)
|
||||
<
|
||||
Return type: any, depending on {tuple}
|
||||
|
||||
get({blob}, {idx} [, {default}]) *get()-blob*
|
||||
Get byte {idx} from |Blob| {blob}. When this byte is not
|
||||
available return {default}. Return -1 when {default} is
|
||||
@@ -5821,8 +5836,8 @@ id({item}) *id()*
|
||||
< prevents {item} from being garbage collected and provides a
|
||||
way to get the {item} from the `id`.
|
||||
|
||||
{item} may be a List, Dictionary, Object, Job, Channel or
|
||||
Blob. If the item is not a permitted type, or it is a null
|
||||
{item} may be a List, Tuple, Dictionary, Object, Job, Channel
|
||||
or Blob. If the item is not a permitted type, or it is a null
|
||||
value, then an empty String is returned.
|
||||
|
||||
Can also be used as a |method|: >
|
||||
@@ -5849,12 +5864,12 @@ index({object}, {expr} [, {start} [, {ic}]]) *index()*
|
||||
Find {expr} in {object} and return its index. See
|
||||
|indexof()| for using a lambda to select the item.
|
||||
|
||||
If {object} is a |List| return the lowest index where the item
|
||||
has a value equal to {expr}. There is no automatic
|
||||
conversion, so the String "4" is different from the Number 4.
|
||||
And the number 4 is different from the Float 4.0. The value
|
||||
of 'ignorecase' is not used here, case matters as indicated by
|
||||
the {ic} argument.
|
||||
If {object} is a |List| or a |Tuple| return the lowest index
|
||||
where the item has a value equal to {expr}. There is no
|
||||
automatic conversion, so the String "4" is different from the
|
||||
Number 4. And the number 4 is different from the Float 4.0.
|
||||
The value of 'ignorecase' is not used here, case matters as
|
||||
indicated by the {ic} argument.
|
||||
|
||||
If {object} is |Blob| return the lowest index where the byte
|
||||
value is equal to {expr}.
|
||||
@@ -5878,11 +5893,11 @@ index({object}, {expr} [, {start} [, {ic}]]) *index()*
|
||||
|
||||
indexof({object}, {expr} [, {opts}]) *indexof()*
|
||||
Returns the index of an item in {object} where {expr} is
|
||||
v:true. {object} must be a |List| or a |Blob|.
|
||||
v:true. {object} must be a |List|, a |Tuple| or a |Blob|.
|
||||
|
||||
If {object} is a |List|, evaluate {expr} for each item in the
|
||||
List until the expression is v:true and return the index of
|
||||
this item.
|
||||
If {object} is a |List| or a |Tuple|, evaluate {expr} for each
|
||||
item in the List until the expression is v:true and return the
|
||||
index of this item.
|
||||
|
||||
If {object} is a |Blob| evaluate {expr} for each byte in the
|
||||
Blob until the expression is v:true and return the index of
|
||||
@@ -5890,11 +5905,11 @@ indexof({object}, {expr} [, {opts}]) *indexof()*
|
||||
|
||||
{expr} must be a |string| or |Funcref|.
|
||||
|
||||
If {expr} is a |string|: If {object} is a |List|, inside
|
||||
{expr} |v:key| has the index of the current List item and
|
||||
|v:val| has the value of the item. If {object} is a |Blob|,
|
||||
inside {expr} |v:key| has the index of the current byte and
|
||||
|v:val| has the byte value.
|
||||
If {expr} is a |string|: If {object} is a |List| or a |Tuple|,
|
||||
inside {expr} |v:key| has the index of the current List or
|
||||
Tuple item and |v:val| has the value of the item. If {object}
|
||||
is a |Blob|, inside {expr} |v:key| has the index of the
|
||||
current byte and |v:val| has the byte value.
|
||||
|
||||
If {expr} is a |Funcref| it must take two arguments:
|
||||
1. the key or the index of the current item.
|
||||
@@ -6204,9 +6219,9 @@ items({dict}) *items()*
|
||||
echo key .. ': ' .. value
|
||||
endfor
|
||||
<
|
||||
A List or a String argument is also supported. In these
|
||||
cases, items() returns a List with the index and the value at
|
||||
the index.
|
||||
A |List|, a |Tuple| or a |String| argument is also supported.
|
||||
In these cases, items() returns a List with the index and the
|
||||
value at the index.
|
||||
|
||||
Can also be used as a |method|: >
|
||||
mydict->items()
|
||||
@@ -6217,16 +6232,17 @@ items({dict}) *items()*
|
||||
job_ functions are documented here: |job-functions-details|
|
||||
|
||||
|
||||
join({list} [, {sep}]) *join()*
|
||||
Join the items in {list} together into one String.
|
||||
join({expr} [, {sep}]) *join()*
|
||||
Join the items in {expr} together into one String. {expr} can
|
||||
be a |List| or a |Tuple|.
|
||||
When {sep} is specified it is put in between the items. If
|
||||
{sep} is omitted a single space is used.
|
||||
Note that {sep} is not added at the end. You might want to
|
||||
add it there too: >
|
||||
let lines = join(mylist, "\n") .. "\n"
|
||||
< String items are used as-is. |Lists| and |Dictionaries| are
|
||||
converted into a string like with |string()|.
|
||||
The opposite function is |split()|.
|
||||
< String items are used as-is. |Lists|, |Tuples| and
|
||||
|Dictionaries| are converted into a string like with
|
||||
|string()|. The opposite function is |split()|.
|
||||
|
||||
Can also be used as a |method|: >
|
||||
mylist->join()
|
||||
@@ -6320,6 +6336,8 @@ json_encode({expr}) *json_encode()*
|
||||
|Funcref| not possible, error
|
||||
|List| as an array (possibly null); when
|
||||
used recursively: []
|
||||
|Tuple| as an array (possibly null); when
|
||||
used recursively: []
|
||||
|Dict| as an object (possibly null); when
|
||||
used recursively: {}
|
||||
|Blob| as an array of the individual bytes
|
||||
@@ -6368,6 +6386,8 @@ len({expr}) *len()* *E701*
|
||||
used, as with |strlen()|.
|
||||
When {expr} is a |List| the number of items in the |List| is
|
||||
returned.
|
||||
When {expr} is a |Tuple| the number of items in the |Tuple| is
|
||||
returned.
|
||||
When {expr} is a |Blob| the number of bytes is returned.
|
||||
When {expr} is a |Dictionary| the number of entries in the
|
||||
|Dictionary| is returned.
|
||||
@@ -6549,6 +6569,25 @@ list2str({list} [, {utf8}]) *list2str()*
|
||||
Return type: |String|
|
||||
|
||||
|
||||
list2tuple({list}) *list2tuple()*
|
||||
Create a Tuple from a shallow copy of the list items.
|
||||
Examples: >
|
||||
list2tuple([1, 2, 3]) returns (1, 2, 3)
|
||||
< |tuple2list()| does the opposite.
|
||||
|
||||
This function doesn't recursively convert all the List items
|
||||
in {list} to a Tuple. Note that the items are identical
|
||||
between the list and the tuple, changing an item changes the
|
||||
contents of both the tuple and the list.
|
||||
|
||||
Returns an empty tuple on error.
|
||||
|
||||
Can also be used as a |method|: >
|
||||
GetList()->list2tuple()
|
||||
<
|
||||
Return type: tuple<{type}> (depending on the given |List|)
|
||||
|
||||
|
||||
listener_add({callback} [, {buf}]) *listener_add()*
|
||||
Add a callback function that will be invoked when changes have
|
||||
been made to buffer {buf}.
|
||||
@@ -7464,11 +7503,12 @@ max({expr}) *max()*
|
||||
Return the maximum value of all items in {expr}. Example: >
|
||||
echo max([apples, pears, oranges])
|
||||
|
||||
< {expr} can be a |List| or a |Dictionary|. For a Dictionary,
|
||||
it returns the maximum of all values in the Dictionary.
|
||||
If {expr} is neither a List nor a Dictionary, or one of the
|
||||
items in {expr} cannot be used as a Number this results in
|
||||
an error. An empty |List| or |Dictionary| results in zero.
|
||||
< {expr} can be a |List|, a |Tuple| or a |Dictionary|. For a
|
||||
Dictionary, it returns the maximum of all values in the
|
||||
Dictionary. If {expr} is neither a List nor a Tuple nor a
|
||||
Dictionary, or one of the items in {expr} cannot be used as a
|
||||
Number this results in an error. An empty |List|, |Tuple|
|
||||
or |Dictionary| results in zero.
|
||||
|
||||
Can also be used as a |method|: >
|
||||
mylist->max()
|
||||
@@ -7555,11 +7595,12 @@ min({expr}) *min()*
|
||||
Return the minimum value of all items in {expr}. Example: >
|
||||
echo min([apples, pears, oranges])
|
||||
|
||||
< {expr} can be a |List| or a |Dictionary|. For a Dictionary,
|
||||
it returns the minimum of all values in the Dictionary.
|
||||
If {expr} is neither a List nor a Dictionary, or one of the
|
||||
items in {expr} cannot be used as a Number this results in
|
||||
an error. An empty |List| or |Dictionary| results in zero.
|
||||
< {expr} can be a |List|, a |Tuple| or a |Dictionary|. For a
|
||||
Dictionary, it returns the minimum of all values in the
|
||||
Dictionary. If {expr} is neither a List nor a Tuple nor a
|
||||
Dictionary, or one of the items in {expr} cannot be used as a
|
||||
Number this results in an error. An empty |List|, |Tuple| or
|
||||
|Dictionary| results in zero.
|
||||
|
||||
Can also be used as a |method|: >
|
||||
mylist->min()
|
||||
@@ -8582,8 +8623,8 @@ readfile({fname} [, {type} [, {max}]])
|
||||
|
||||
reduce({object}, {func} [, {initial}]) *reduce()* *E998*
|
||||
{func} is called for every item in {object}, which can be a
|
||||
|String|, |List| or a |Blob|. {func} is called with two
|
||||
arguments: the result so far and current item. After
|
||||
|String|, |List|, |Tuple| or a |Blob|. {func} is called with
|
||||
two arguments: the result so far and current item. After
|
||||
processing all items the result is returned. *E1132*
|
||||
|
||||
{initial} is the initial result. When omitted, the first item
|
||||
@@ -8904,16 +8945,16 @@ repeat({expr}, {count}) *repeat()*
|
||||
result. Example: >
|
||||
:let separator = repeat('-', 80)
|
||||
< When {count} is zero or negative the result is empty.
|
||||
When {expr} is a |List| or a |Blob| the result is {expr}
|
||||
concatenated {count} times. Example: >
|
||||
When {expr} is a |List|, a |Tuple| or a |Blob| the result is
|
||||
{expr} concatenated {count} times. Example: >
|
||||
:let longlist = repeat(['a', 'b'], 3)
|
||||
< Results in ['a', 'b', 'a', 'b', 'a', 'b'].
|
||||
|
||||
Can also be used as a |method|: >
|
||||
mylist->repeat(count)
|
||||
<
|
||||
Return type: |String|, |Blob| or list<{type}> depending on
|
||||
{expr}
|
||||
Return type: |String|, |Blob|, list<{type}> or tuple<{type}>
|
||||
depending on {expr}
|
||||
|
||||
|
||||
resolve({filename}) *resolve()* *E655*
|
||||
@@ -8940,18 +8981,19 @@ resolve({filename}) *resolve()* *E655*
|
||||
|
||||
reverse({object}) *reverse()*
|
||||
Reverse the order of items in {object}. {object} can be a
|
||||
|List|, a |Blob| or a |String|. For a List and a Blob the
|
||||
items are reversed in-place and {object} is returned.
|
||||
|List|, a |Tuple|, a |Blob| or a |String|. For a List and a
|
||||
Blob the items are reversed in-place and {object} is returned.
|
||||
For a Tuple, a new Tuple is returned.
|
||||
For a String a new String is returned.
|
||||
Returns zero if {object} is not a List, Blob or a String.
|
||||
If you want a List or Blob to remain unmodified make a copy
|
||||
first: >
|
||||
Returns zero if {object} is not a List, Tuple, Blob or a
|
||||
String. If you want a List or Blob to remain unmodified make
|
||||
a copy first: >
|
||||
:let revlist = reverse(copy(mylist))
|
||||
< Can also be used as a |method|: >
|
||||
mylist->reverse()
|
||||
<
|
||||
Return type: |String|, |Blob| or list<{type}> depending on
|
||||
{object}
|
||||
Return type: |String|, |Blob|, list<{type}> or tuple<{type}>
|
||||
depending on {object}
|
||||
|
||||
|
||||
round({expr}) *round()*
|
||||
@@ -10304,7 +10346,7 @@ slice({expr}, {start} [, {end}]) *slice()*
|
||||
Can also be used as a |method|: >
|
||||
GetList()->slice(offset)
|
||||
<
|
||||
Return type: list<{type}>
|
||||
Return type: list<{type}> or tuple<{type}>
|
||||
|
||||
|
||||
sort({list} [, {how} [, {dict}]]) *sort()* *E702*
|
||||
@@ -10916,15 +10958,16 @@ string({expr}) *string()*
|
||||
Funcref function('name')
|
||||
Blob 0z00112233.44556677.8899
|
||||
List [item, item]
|
||||
Tuple (item, item)
|
||||
Dictionary {key: value, key: value}
|
||||
Class class SomeName
|
||||
Object object of SomeName {lnum: 1, col: 3}
|
||||
Enum enum EnumName
|
||||
EnumValue enum name.value {name: str, ordinal: nr}
|
||||
|
||||
When a |List| or |Dictionary| has a recursive reference it is
|
||||
replaced by "[...]" or "{...}". Using eval() on the result
|
||||
will then fail.
|
||||
When a |List|, |Tuple| or |Dictionary| has a recursive
|
||||
reference it is replaced by "[...]" or "(...)" or "{...}".
|
||||
Using eval() on the result will then fail.
|
||||
|
||||
For an object, invokes the string() method to get a textual
|
||||
representation of the object. If the method is not present,
|
||||
@@ -11878,6 +11921,25 @@ trunc({expr}) *trunc()*
|
||||
Return type: |Float|
|
||||
|
||||
|
||||
tuple2list({list}) *tuple2list()*
|
||||
Create a List from a shallow copy of the tuple items.
|
||||
Examples: >
|
||||
tuple2list((1, 2, 3)) returns [1, 2, 3]
|
||||
< |list2tuple()| does the opposite.
|
||||
|
||||
This function doesn't recursively convert all the Tuple items
|
||||
in {tuple} to a List. Note that the items are identical
|
||||
between the list and the tuple, changing an item changes the
|
||||
contents of both the tuple and the list.
|
||||
|
||||
Returns an empty list on error.
|
||||
|
||||
Can also be used as a |method|: >
|
||||
GetTuple()->tuple2list()
|
||||
<
|
||||
Return type: list<{type}> (depending on the given |Tuple|)
|
||||
|
||||
|
||||
*type()*
|
||||
type({expr}) The result is a Number representing the type of {expr}.
|
||||
Instead of using the number directly, it is better to use the
|
||||
@@ -11898,6 +11960,7 @@ type({expr}) The result is a Number representing the type of {expr}.
|
||||
Typealias: 14 |v:t_typealias|
|
||||
Enum: 15 |v:t_enum|
|
||||
EnumValue: 16 |v:t_enumvalue|
|
||||
Tuple: 17 |v:t_tuple|
|
||||
For backward compatibility, this method can be used: >
|
||||
:if type(myvar) == type(0)
|
||||
:if type(myvar) == type("")
|
||||
|
||||
+292
-60
@@ -1,4 +1,4 @@
|
||||
*eval.txt* For Vim version 9.1. Last change: 2025 Feb 23
|
||||
*eval.txt* For Vim version 9.1. Last change: 2025 Mar 23
|
||||
|
||||
|
||||
VIM REFERENCE MANUAL by Bram Moolenaar
|
||||
@@ -21,9 +21,10 @@ a remark is given.
|
||||
1.1 Variable types
|
||||
1.2 Function references |Funcref|
|
||||
1.3 Lists |Lists|
|
||||
1.4 Dictionaries |Dictionaries|
|
||||
1.5 Blobs |Blobs|
|
||||
1.6 More about variables |more-variables|
|
||||
1.4 Tuples |Tuples|
|
||||
1.5 Dictionaries |Dictionaries|
|
||||
1.6 Blobs |Blobs|
|
||||
1.7 More about variables |more-variables|
|
||||
2. Expression syntax |expression-syntax|
|
||||
3. Internal variable |internal-variables|
|
||||
4. Builtin Functions |functions|
|
||||
@@ -46,8 +47,8 @@ Profiling is documented at |profiling|.
|
||||
|
||||
1.1 Variable types ~
|
||||
*E712* *E896* *E897* *E899* *E1098*
|
||||
*E1107* *E1135* *E1138*
|
||||
There are ten types of variables:
|
||||
*E1107* *E1135* *E1138* *E1523*
|
||||
There are eleven types of variables:
|
||||
|
||||
*Number* *Integer*
|
||||
Number A 32 or 64 bit signed number. |expr-number|
|
||||
@@ -63,6 +64,10 @@ String A NUL terminated string of 8-bit unsigned characters (bytes).
|
||||
List An ordered sequence of items, see |List| for details.
|
||||
Example: [1, 2, ['a', 'b']]
|
||||
|
||||
Tuple An ordered immutable sequence of items, see |Tuple| for
|
||||
details.
|
||||
Example: (1, 2, ('a', 'b'))
|
||||
|
||||
Dictionary An associative, unordered array: Each entry has a key and a
|
||||
value. |Dictionary|
|
||||
Examples:
|
||||
@@ -165,16 +170,17 @@ A List, Dictionary or Float is not a Number or String, thus evaluate to FALSE.
|
||||
|
||||
*E611* *E745* *E728* *E703* *E729* *E730* *E731* *E908* *E910*
|
||||
*E913* *E974* *E975* *E976* *E1319* *E1320* *E1321* *E1322*
|
||||
*E1323* *E1324*
|
||||
|List|, |Dictionary|, |Funcref|, |Job|, |Channel|, |Blob|, |Class| and
|
||||
|object| types are not automatically converted.
|
||||
*E1323* *E1324* *E1520* *E1522*
|
||||
|List|, |Tuple|, |Dictionary|, |Funcref|, |Job|, |Channel|, |Blob|, |Class|
|
||||
and |object| types are not automatically converted.
|
||||
|
||||
*E805* *E806* *E808*
|
||||
When mixing Number and Float the Number is converted to Float. Otherwise
|
||||
there is no automatic conversion of Float. You can use str2float() for String
|
||||
to Float, printf() for Float to String and float2nr() for Float to Number.
|
||||
|
||||
*E362* *E891* *E892* *E893* *E894* *E907* *E911* *E914*
|
||||
*E362* *E891* *E892* *E893* *E894*
|
||||
*E907* *E911* *E914* *E1521*
|
||||
When expecting a Float a Number can also be used, but nothing else.
|
||||
|
||||
*no-type-checking*
|
||||
@@ -267,9 +273,9 @@ position in the sequence.
|
||||
|
||||
List creation ~
|
||||
*E696* *E697*
|
||||
A List is created with a comma-separated list of items in square brackets.
|
||||
A List is created with a comma-separated sequence of items in square brackets.
|
||||
Examples: >
|
||||
:let mylist = [1, two, 3, "four"]
|
||||
:let mylist = [1, "two", 3, "four"]
|
||||
:let emptylist = []
|
||||
|
||||
An item can be any expression. Using a List for an item creates a
|
||||
@@ -327,13 +333,13 @@ similar to -1. >
|
||||
:let otherlist = mylist[:] " make a copy of the List
|
||||
|
||||
Notice that the last index is inclusive. If you prefer using an exclusive
|
||||
index use the |slice()| method.
|
||||
index use the |slice()| function.
|
||||
|
||||
If the first index is beyond the last item of the List or the second item is
|
||||
If the first index is beyond the last item of the List or the last index is
|
||||
before the first item, the result is an empty list. There is no error
|
||||
message.
|
||||
|
||||
If the second index is equal to or greater than the length of the list the
|
||||
If the last index is equal to or greater than the length of the list the
|
||||
length minus one is used: >
|
||||
:let mylist = [0, 1, 2, 3]
|
||||
:echo mylist[2:8] " result: [2, 3]
|
||||
@@ -463,8 +469,8 @@ Changing the order of items in a list: >
|
||||
|
||||
For loop ~
|
||||
|
||||
The |:for| loop executes commands for each item in a List, String or Blob.
|
||||
A variable is set to each item in sequence. Example with a List: >
|
||||
The |:for| loop executes commands for each item in a List, Tuple, String or
|
||||
Blob. A variable is set to each item in sequence. Example with a List: >
|
||||
:for item in mylist
|
||||
: call Doit(item)
|
||||
:endfor
|
||||
@@ -497,6 +503,8 @@ It is also possible to put remaining items in a List variable: >
|
||||
: endif
|
||||
:endfor
|
||||
|
||||
For a Tuple one tuple item at a time is used.
|
||||
|
||||
For a Blob one byte at a time is used.
|
||||
|
||||
For a String one character, including any composing characters, is used as a
|
||||
@@ -527,8 +535,206 @@ Don't forget that a combination of features can make things simple. For
|
||||
example, to add up all the numbers in a list: >
|
||||
:exe 'let sum = ' .. join(nrlist, '+')
|
||||
|
||||
1.4 Tuples ~
|
||||
*tuple* *Tuple* *Tuples*
|
||||
*E1532* *E1533*
|
||||
A Tuple is an ordered sequence of items. An item can be of any type. Items
|
||||
can be accessed by their index number. A Tuple is immutable.
|
||||
|
||||
1.4 Dictionaries ~
|
||||
A Tuple uses less memory compared to a List and provides O(1) lookup time.
|
||||
|
||||
Tuple creation ~
|
||||
*E1526* *E1527*
|
||||
A Tuple is created with a comma-separated sequence of items in parentheses.
|
||||
Examples: >
|
||||
:let mytuple = (1, "two", 3, "four")
|
||||
:let tuple = (5,)
|
||||
:let emptytuple = ()
|
||||
|
||||
An item can be any expression. If there is only one item in the tuple, then
|
||||
the item must be followed by a comma.
|
||||
|
||||
Using a Tuple for an item creates a Tuple of Tuples: >
|
||||
:let nesttuple = ((11, 12), (21, 22), (31, 32))
|
||||
|
||||
|
||||
Tuple index ~
|
||||
*tuple-index* *E1519*
|
||||
An item in the Tuple can be accessed by putting the index in square brackets
|
||||
after the Tuple. Indexes are zero-based, thus the first item has index zero.
|
||||
>
|
||||
:let item = mytuple[0] " get the first item: 1
|
||||
:let item = mytuple[2] " get the third item: 3
|
||||
|
||||
When the resulting item is a tuple this can be repeated: >
|
||||
:let item = nesttuple[0][1] " get the first tuple, second item: 12
|
||||
<
|
||||
A negative index is counted from the end. Index -1 refers to the last item in
|
||||
the Tuple, -2 to the last but one item, etc. >
|
||||
:let last = mytuple[-1] " get the last item: "four"
|
||||
|
||||
To avoid an error for an invalid index use the |get()| function. When an item
|
||||
is not available it returns zero or the default value you specify: >
|
||||
:echo get(mytuple, idx)
|
||||
:echo get(mytuple, idx, "NONE")
|
||||
|
||||
|
||||
Tuple concatenation ~
|
||||
*tuple-concatenation*
|
||||
Two tuples can be concatenated with the "+" operator: >
|
||||
:let longtuple = mytuple + (5, 6)
|
||||
:let longtuple = (5, 6) + mytuple
|
||||
To prepend or append an item, turn it into a tuple by putting () around it.
|
||||
The item must be followed by a comma.
|
||||
|
||||
*E1540*
|
||||
Two variadic tuples with same item type can be concatenated but with different
|
||||
item types cannot be concatenated. Examples: >
|
||||
var a: tuple<...list<number>> = (1, 2)
|
||||
var b: tuple<...list<string>> = ('a', 'b')
|
||||
echo a + b # not allowed
|
||||
|
||||
var a: tuple<number, number> = (1, 2)
|
||||
var b: tuple<...list<string>> = ('a', 'b')
|
||||
echo a + b # allowed
|
||||
|
||||
var a: tuple<...list<number>> = (1, 2)
|
||||
var b: tuple<number, number> = (3, 4)
|
||||
echo a + b # not allowed
|
||||
|
||||
var a: tuple<...list<number>> = (1, 2)
|
||||
var b: tuple<number, ...list<number>> = (3, 4)
|
||||
echo a + b # not allowed
|
||||
<
|
||||
Note that a tuple is immutable and items cannot be added or removed from a
|
||||
tuple.
|
||||
|
||||
|
||||
Subtuple ~
|
||||
*subtuple*
|
||||
A part of the Tuple can be obtained by specifying the first and last index,
|
||||
separated by a colon in square brackets: >
|
||||
:let shorttuple = mytuple[2:-1] " get Tuple (3, "four")
|
||||
|
||||
Omitting the first index is similar to zero. Omitting the last index is
|
||||
similar to -1. >
|
||||
:let endtuple = mytuple[2:] " from item 2 to the end: (3, "four")
|
||||
:let shorttuple = mytuple[2:2] " Tuple with one item: (3,)
|
||||
:let othertuple = mytuple[:] " make a copy of the Tuple
|
||||
|
||||
Notice that the last index is inclusive. If you prefer using an exclusive
|
||||
index, use the |slice()| function.
|
||||
|
||||
If the first index is beyond the last item of the Tuple or the last index is
|
||||
before the first item, the result is an empty tuple. There is no error
|
||||
message.
|
||||
|
||||
If the last index is equal to or greater than the length of the tuple, the
|
||||
length minus one is used: >
|
||||
:let mytuple = (0, 1, 2, 3)
|
||||
:echo mytuple[2:8] " result: (2, 3)
|
||||
|
||||
NOTE: mytuple[s:e] means using the variable "s:e" as index. Watch out for
|
||||
using a single letter variable before the ":". Insert a space when needed:
|
||||
mytuple[s : e].
|
||||
|
||||
|
||||
Tuple identity ~
|
||||
*tuple-identity*
|
||||
When variable "aa" is a tuple and you assign it to another variable "bb", both
|
||||
variables refer to the same tuple: >
|
||||
:let aa = (1, 2, 3)
|
||||
:let bb = aa
|
||||
<
|
||||
|
||||
Making a copy of a tuple is done with the |copy()| function. Using [:] also
|
||||
works, as explained above. This creates a shallow copy of the tuple: For
|
||||
example, changing a list item in the tuple will also change the item in the
|
||||
copied tuple: >
|
||||
:let aa = ([1, 'a'], 2, 3)
|
||||
:let bb = copy(aa)
|
||||
:let aa[0][1] = 'aaa'
|
||||
:echo aa
|
||||
< ([1, aaa], 2, 3) >
|
||||
:echo bb
|
||||
< ([1, aaa], 2, 3)
|
||||
|
||||
To make a completely independent tuple, use |deepcopy()|. This also makes a
|
||||
copy of the values in the tuple, recursively. Up to a hundred levels deep.
|
||||
|
||||
The operator "is" can be used to check if two variables refer to the same
|
||||
Tuple. "isnot" does the opposite. In contrast, "==" compares if two tuples
|
||||
have the same value. >
|
||||
:let atuple = (1, 2, 3)
|
||||
:let btuple = (1, 2, 3)
|
||||
:echo atuple is btuple
|
||||
< 0 >
|
||||
:echo atuple == btuple
|
||||
< 1
|
||||
|
||||
Note about comparing tuples: Two tuples are considered equal if they have the
|
||||
same length and all items compare equal, as with using "==". There is one
|
||||
exception: When comparing a number with a string they are considered
|
||||
different. There is no automatic type conversion, as with using "==" on
|
||||
variables. Example: >
|
||||
echo 4 == "4"
|
||||
< 1 >
|
||||
echo (4,) == ("4",)
|
||||
< 0
|
||||
|
||||
Thus comparing Tuples is more strict than comparing numbers and strings. You
|
||||
can compare simple values this way too by putting them in a tuple: >
|
||||
|
||||
:let a = 5
|
||||
:let b = "5"
|
||||
:echo a == b
|
||||
< 1 >
|
||||
:echo (a,) == (b,)
|
||||
< 0
|
||||
|
||||
|
||||
Tuple unpack ~
|
||||
|
||||
To unpack the items in a tuple to individual variables, put the variables in
|
||||
square brackets, like list items: >
|
||||
:let [var1, var2] = mytuple
|
||||
|
||||
When the number of variables does not match the number of items in the tuple
|
||||
this produces an error. To handle any extra items from the tuple, append ";"
|
||||
and a variable name (which will then be of type tuple): >
|
||||
:let [var1, var2; rest] = mytuple
|
||||
|
||||
This works like: >
|
||||
:let var1 = mytuple[0]
|
||||
:let var2 = mytuple[1]
|
||||
:let rest = mytuple[2:]
|
||||
|
||||
Except that there is no error if there are only two items. "rest" will be an
|
||||
empty tuple then.
|
||||
|
||||
|
||||
Tuple functions ~
|
||||
*E1536*
|
||||
Functions that are useful with a Tuple: >
|
||||
:let xs = count(tuple, 'x') " count number of 'x's in tuple
|
||||
:if empty(tuple) " check if tuple is empty
|
||||
:let i = index(tuple, 'x') " index of first 'x' in tuple
|
||||
:let l = items(tuple) " list of items in a tuple
|
||||
:let string = join(tuple, ', ') " create string from tuple items
|
||||
:let l = len(tuple) " number of items in tuple
|
||||
:let big = max(tuple) " maximum value in tuple
|
||||
:let small = min(tuple) " minimum value in tuple
|
||||
:let r = repeat(tuple, n) " repeat a tuple n times
|
||||
:let r = reverse(tuple) " reverse a tuple
|
||||
:let s = slice(tuple, n1, n2) " slice a tuple
|
||||
:let s = string(tuple) " String representation of tuple
|
||||
:let l = tuple2list(tuple) " convert a tuple to list
|
||||
:let t = list2tuple(list) " convert a list to tuple
|
||||
<
|
||||
*E1524*
|
||||
A tuple cannot be used with the |map()|, |mapnew()| and |filter()| functions.
|
||||
|
||||
1.5 Dictionaries ~
|
||||
*dict* *Dict* *Dictionaries* *Dictionary*
|
||||
A Dictionary is an associative array: Each entry has a key and a value. The
|
||||
entry can be located with the key. The entries are stored without a specific
|
||||
@@ -537,10 +743,10 @@ ordering.
|
||||
|
||||
Dictionary creation ~
|
||||
*E720* *E721* *E722* *E723*
|
||||
A Dictionary is created with a comma-separated list of entries in curly
|
||||
A Dictionary is created with a comma-separated sequence of entries in curly
|
||||
braces. Each entry has a key and a value, separated by a colon. Each key can
|
||||
only appear once. Examples: >
|
||||
:let mydict = {1: 'one', 2: 'two', 3: 'three'}
|
||||
:let mydict = {'one': 1, 'two': 2, 'three': 3}
|
||||
:let emptydict = {}
|
||||
< *E713* *E716* *E717*
|
||||
A key is always a String. You can use a Number, it will be converted to a
|
||||
@@ -570,8 +776,11 @@ An extra comma after the last entry is ignored.
|
||||
Accessing entries ~
|
||||
|
||||
The normal way to access an entry is by putting the key in square brackets: >
|
||||
:let mydict = {'one': 1, 'two': 2, 'three': 3}
|
||||
:let val = mydict["one"]
|
||||
:let mydict["four"] = 4
|
||||
:let val = mydict.one
|
||||
:let mydict.four = 4
|
||||
|
||||
You can add new entries to an existing Dictionary this way, unlike Lists.
|
||||
|
||||
@@ -709,7 +918,7 @@ Functions that can be used with a Dictionary: >
|
||||
:call map(dict, '">> " .. v:val') " prepend ">> " to each item
|
||||
|
||||
|
||||
1.5 Blobs ~
|
||||
1.6 Blobs ~
|
||||
*blob* *Blob* *Blobs* *E978*
|
||||
A Blob is a binary object. It can be used to read an image from a file and
|
||||
send it over a channel, for example.
|
||||
@@ -856,7 +1065,7 @@ Making a copy of a Blob is done with the |copy()| function. Using [:] also
|
||||
works, as explained above.
|
||||
|
||||
|
||||
1.6 More about variables ~
|
||||
1.7 More about variables ~
|
||||
*more-variables*
|
||||
If you need to know the type of a variable or expression, use the |type()|
|
||||
function.
|
||||
@@ -907,16 +1116,18 @@ Expression syntax summary, from least to most significant:
|
||||
etc. As above, append ? for ignoring case, # for
|
||||
matching case
|
||||
|
||||
expr5 is expr5 same |List|, |Dictionary| or |Blob| instance
|
||||
expr5 isnot expr5 different |List|, |Dictionary| or |Blob|
|
||||
expr5 is expr5 same |List|, |Tuple|, |Dictionary| or |Blob|
|
||||
instance
|
||||
expr5 isnot expr5 different |List|, |Tuple|, |Dictionary| or
|
||||
|Blob| instance
|
||||
|
||||
|expr5| expr6
|
||||
expr6 << expr6 bitwise left shift
|
||||
expr6 >> expr6 bitwise right shift
|
||||
|
||||
|expr6| expr7
|
||||
expr7 + expr7 ... number addition, list or blob concatenation
|
||||
expr7 + expr7 ... number addition, list or tuple or blob
|
||||
concatenation
|
||||
expr7 - expr7 ... number subtraction
|
||||
expr7 . expr7 ... string concatenation
|
||||
expr7 .. expr7 ... string concatenation
|
||||
@@ -935,8 +1146,10 @@ Expression syntax summary, from least to most significant:
|
||||
+ expr9 unary plus
|
||||
|
||||
|expr10| expr11
|
||||
expr10[expr1] byte of a String or item of a |List|
|
||||
expr10[expr1] byte of a String or item of a |List| or
|
||||
|Tuple|
|
||||
expr10[expr1 : expr1] substring of a String or sublist of a |List|
|
||||
or a slice of a |Tuple|
|
||||
expr10.name entry in a |Dictionary|
|
||||
expr10(expr1, ...) function call with |Funcref| variable
|
||||
expr10->name(expr1, ...) |method| call
|
||||
@@ -945,6 +1158,7 @@ Expression syntax summary, from least to most significant:
|
||||
"string" string constant, backslash is special
|
||||
'string' string constant, ' is doubled
|
||||
[expr1, ...] |List|
|
||||
(expr1, ...) |Tuple|
|
||||
{expr1: expr1, ...} |Dictionary|
|
||||
#{key: expr1, ...} legacy |Dictionary|
|
||||
&option option value
|
||||
@@ -1101,10 +1315,11 @@ Examples:
|
||||
"abc" == "Abc" evaluates to 1 if 'ignorecase' is set, 0 otherwise
|
||||
NOTE: In |Vim9| script 'ignorecase' is not used.
|
||||
|
||||
*E691* *E692*
|
||||
*E691* *E692* *E1517* *E1518*
|
||||
A |List| can only be compared with a |List| and only "equal", "not equal",
|
||||
"is" and "isnot" can be used. This compares the values of the list,
|
||||
recursively. Ignoring case means case is ignored when comparing item values.
|
||||
Same applies for a |Tuple|.
|
||||
|
||||
*E735* *E736*
|
||||
A |Dictionary| can only be compared with a |Dictionary| and only "equal", "not
|
||||
@@ -1124,12 +1339,13 @@ Dictionary and arguments, use |get()| to get the function name: >
|
||||
if get(Part1, 'name') == get(Part2, 'name')
|
||||
" Part1 and Part2 refer to the same function
|
||||
< *E1037*
|
||||
Using "is" or "isnot" with a |List|, |Dictionary| or |Blob| checks whether
|
||||
the expressions are referring to the same |List|, |Dictionary| or |Blob|
|
||||
instance. A copy of a |List| is different from the original |List|. When
|
||||
using "is" without a |List|, |Dictionary| or |Blob|, it is equivalent to
|
||||
using "equal", using "isnot" equivalent to using "not equal". Except that
|
||||
a different type means the values are different: >
|
||||
Using "is" or "isnot" with a |List|, |Tuple|, |Dictionary| or |Blob| checks
|
||||
whether the expressions are referring to the same |List|, |Tuple|,
|
||||
|Dictionary| or |Blob| instance. A copy of a |List| or |Tuple| is different
|
||||
from the original |List| or |Tuple|. When using "is" without a |List|,
|
||||
|Tuple|, |Dictionary| or |Blob|, it is equivalent to using "equal", using
|
||||
"isnot" is equivalent to using "not equal". Except that a different type
|
||||
means the values are different: >
|
||||
echo 4 == '4'
|
||||
1
|
||||
echo 4 is '4'
|
||||
@@ -1147,7 +1363,7 @@ that: >
|
||||
because 'x' converted to a Number is zero. However: >
|
||||
echo [0] == ['x']
|
||||
0
|
||||
Inside a List or Dictionary this conversion is not used.
|
||||
Inside a List or Tuple or Dictionary this conversion is not used.
|
||||
|
||||
In |Vim9| script the types must match.
|
||||
|
||||
@@ -1191,13 +1407,14 @@ topmost bit (sometimes called the sign bit) is cleared. If the right operand
|
||||
|
||||
expr6 and expr7 *expr6* *expr7* *E1036* *E1051*
|
||||
---------------
|
||||
expr7 + expr7 Number addition, |List| or |Blob| concatenation *expr-+*
|
||||
*expr-+*
|
||||
expr7 + expr7 Number addition, |List| or |Tuple| or |Blob| concatenation
|
||||
expr7 - expr7 Number subtraction *expr--*
|
||||
expr7 . expr7 String concatenation *expr-.*
|
||||
expr7 .. expr7 String concatenation *expr-..*
|
||||
|
||||
For |Lists| only "+" is possible and then both expr7 must be a list. The
|
||||
result is a new list with the two lists Concatenated.
|
||||
result is a new list with the two lists concatenated. Same for a |Tuple|.
|
||||
|
||||
For String concatenation ".." is preferred, since "." is ambiguous, it is also
|
||||
used for |Dict| member access and floating point numbers.
|
||||
@@ -1295,7 +1512,8 @@ in any order. E.g., these are all possible:
|
||||
expr10->(expr1, ...)[expr1]
|
||||
Evaluation is always from left to right.
|
||||
|
||||
expr10[expr1] item of String or |List| *expr-[]* *E111*
|
||||
*expr-[]* *E111*
|
||||
expr10[expr1] item of String or |List| or |Tuple|
|
||||
*E909* *subscript* *E1062*
|
||||
In legacy Vim script:
|
||||
If expr10 is a Number or String this results in a String that contains the
|
||||
@@ -1328,6 +1546,8 @@ Generally, if a |List| index is equal to or higher than the length of the
|
||||
|List|, or more negative than the length of the |List|, this results in an
|
||||
error.
|
||||
|
||||
A |Tuple| index is similar to a |List| index as explained above.
|
||||
|
||||
|
||||
expr10[expr1a : expr1b] substring or |sublist| *expr-[:]* *substring*
|
||||
|
||||
@@ -1369,6 +1589,7 @@ just above. Also see |sublist| below. Examples: >
|
||||
:let l = mylist[:3] " first four items
|
||||
:let l = mylist[4:4] " List with one item
|
||||
:let l = mylist[:] " shallow copy of a List
|
||||
A |Tuple| slice is similar to a |List| slice.
|
||||
|
||||
If expr10 is a |Blob| this results in a new |Blob| with the bytes in the
|
||||
indexes expr1a and expr1b, inclusive. Examples: >
|
||||
@@ -2615,6 +2836,8 @@ v:t_typealias Value of |typealias| type. Read-only. See: |type()|
|
||||
v:t_enum Value of |enum| type. Read-only. See: |type()|
|
||||
*v:t_enumvalue* *t_enumvalue-variable*
|
||||
v:t_enumvalue Value of |enumvalue| type. Read-only. See: |type()|
|
||||
*v:t_tuple* *t_tuple-variable*
|
||||
v:t_tuple Value of |Tuple| type. Read-only. See: |type()|
|
||||
|
||||
*v:termresponse* *termresponse-variable*
|
||||
v:termresponse The escape sequence returned by the terminal for the |t_RV|
|
||||
@@ -2934,13 +3157,13 @@ declarations and assignments do not use a command. |vim9-declaration|
|
||||
:let &g:{option-name} -= {expr1}
|
||||
Like above, but only set the global value of an option
|
||||
(if there is one). Works like |:setglobal|.
|
||||
*E1093*
|
||||
*E1093* *E1537* *E1538* *E1535*
|
||||
:let [{name1}, {name2}, ...] = {expr1} *:let-unpack* *E687* *E688*
|
||||
{expr1} must evaluate to a |List|. The first item in
|
||||
the list is assigned to {name1}, the second item to
|
||||
{name2}, etc.
|
||||
{expr1} must evaluate to a |List| or a |Tuple|. The
|
||||
first item in the list or tuple is assigned to
|
||||
{name1}, the second item to {name2}, etc.
|
||||
The number of names must match the number of items in
|
||||
the |List|.
|
||||
the |List| or |Tuple|.
|
||||
Each name can be one of the items of the ":let"
|
||||
command as mentioned above.
|
||||
Example: >
|
||||
@@ -2957,16 +3180,22 @@ declarations and assignments do not use a command. |vim9-declaration|
|
||||
:let [{name1}, {name2}, ...] .= {expr1}
|
||||
:let [{name1}, {name2}, ...] += {expr1}
|
||||
:let [{name1}, {name2}, ...] -= {expr1}
|
||||
Like above, but append/add/subtract the value for each
|
||||
|List| item.
|
||||
:let [{name1}, {name2}, ...] *= {expr1}
|
||||
:let [{name1}, {name2}, ...] /= {expr1}
|
||||
:let [{name1}, {name2}, ...] %= {expr1}
|
||||
Like above, but append, add, subtract, multiply,
|
||||
divide, or modulo the value for each |List| or |Tuple|
|
||||
item.
|
||||
|
||||
:let [{name}, ..., ; {lastname}] = {expr1} *E452*
|
||||
Like |:let-unpack| above, but the |List| may have more
|
||||
items than there are names. A list of the remaining
|
||||
items is assigned to {lastname}. If there are no
|
||||
remaining items {lastname} is set to an empty list.
|
||||
Like |:let-unpack| above, but the |List| or |Tuple|
|
||||
may have more items than there are names. A list or a
|
||||
tuple of the remaining items is assigned to
|
||||
{lastname}. If there are no remaining items,
|
||||
{lastname} is set to an empty list or tuple.
|
||||
Example: >
|
||||
:let [a, b; rest] = ["aval", "bval", 3, 4]
|
||||
:let [a, b; rest] = ("aval", "bval", 3, 4)
|
||||
<
|
||||
:let [{name}, ..., ; {lastname}] .= {expr1}
|
||||
:let [{name}, ..., ; {lastname}] += {expr1}
|
||||
@@ -3161,23 +3390,26 @@ text...
|
||||
get an error message: "E940: Cannot lock or unlock
|
||||
variable {name}".
|
||||
|
||||
[depth] is relevant when locking a |List| or
|
||||
|Dictionary|. It specifies how deep the locking goes:
|
||||
[depth] is relevant when locking a |List|, a |Tuple|
|
||||
or a |Dictionary|. It specifies how deep the locking
|
||||
goes:
|
||||
0 Lock the variable {name} but not its
|
||||
value.
|
||||
1 Lock the |List| or |Dictionary| itself,
|
||||
cannot add or remove items, but can
|
||||
still change their values.
|
||||
1 Lock the |List| or |Tuple| or
|
||||
|Dictionary| itself, cannot add or
|
||||
remove items, but can still change
|
||||
their values.
|
||||
2 Also lock the values, cannot change
|
||||
the items. If an item is a |List| or
|
||||
|Dictionary|, cannot add or remove
|
||||
items, but can still change the
|
||||
|Tuple| or |Dictionary|, cannot add or
|
||||
remove items, but can still change the
|
||||
values.
|
||||
3 Like 2 but for the |List| /
|
||||
|Dictionary| in the |List| /
|
||||
3 Like 2 but for the |List| / |Tuple| /
|
||||
|Dictionary| in the |List| / |Tuple| /
|
||||
|Dictionary|, one level deeper.
|
||||
The default [depth] is 2, thus when {name} is a |List|
|
||||
or |Dictionary| the values cannot be changed.
|
||||
The default [depth] is 2, thus when {name} is a
|
||||
|List|, a |Tuple| or a |Dictionary| the values cannot
|
||||
be changed.
|
||||
|
||||
Example with [depth] 0: >
|
||||
let mylist = [1, 2, 3]
|
||||
@@ -3282,7 +3514,7 @@ text...
|
||||
:endfo[r] *:endfo* *:endfor*
|
||||
Repeat the commands between `:for` and `:endfor` for
|
||||
each item in {object}. {object} can be a |List|,
|
||||
a |Blob| or a |String|. *E1177*
|
||||
a |Tuple|, a |Blob| or a |String|. *E1177*
|
||||
|
||||
Variable {var} is set to the value of each item.
|
||||
In |Vim9| script the loop variable must not have been
|
||||
|
||||
@@ -4592,9 +4592,33 @@ E1513 message.txt /*E1513*
|
||||
E1514 options.txt /*E1514*
|
||||
E1515 builtin.txt /*E1515*
|
||||
E1516 builtin.txt /*E1516*
|
||||
E1517 eval.txt /*E1517*
|
||||
E1518 eval.txt /*E1518*
|
||||
E1519 eval.txt /*E1519*
|
||||
E152 helphelp.txt /*E152*
|
||||
E1520 eval.txt /*E1520*
|
||||
E1521 eval.txt /*E1521*
|
||||
E1522 eval.txt /*E1522*
|
||||
E1523 eval.txt /*E1523*
|
||||
E1524 eval.txt /*E1524*
|
||||
E1525 builtin.txt /*E1525*
|
||||
E1526 eval.txt /*E1526*
|
||||
E1527 eval.txt /*E1527*
|
||||
E1528 vim9.txt /*E1528*
|
||||
E1529 vim9.txt /*E1529*
|
||||
E153 helphelp.txt /*E153*
|
||||
E1530 vim9.txt /*E1530*
|
||||
E1531 vim9.txt /*E1531*
|
||||
E1532 eval.txt /*E1532*
|
||||
E1533 eval.txt /*E1533*
|
||||
E1534 vim9.txt /*E1534*
|
||||
E1535 eval.txt /*E1535*
|
||||
E1536 eval.txt /*E1536*
|
||||
E1537 eval.txt /*E1537*
|
||||
E1538 eval.txt /*E1538*
|
||||
E1539 vim9.txt /*E1539*
|
||||
E154 helphelp.txt /*E154*
|
||||
E1540 eval.txt /*E1540*
|
||||
E155 sign.txt /*E155*
|
||||
E156 sign.txt /*E156*
|
||||
E157 sign.txt /*E157*
|
||||
@@ -5785,6 +5809,8 @@ TextChangedP autocmd.txt /*TextChangedP*
|
||||
TextChangedT autocmd.txt /*TextChangedT*
|
||||
TextYankPost autocmd.txt /*TextYankPost*
|
||||
Transact-SQL ft_sql.txt /*Transact-SQL*
|
||||
Tuple eval.txt /*Tuple*
|
||||
Tuples eval.txt /*Tuples*
|
||||
U undo.txt /*U*
|
||||
UTF-8 mbyte.txt /*UTF-8*
|
||||
UTF8-xterm mbyte.txt /*UTF8-xterm*
|
||||
@@ -7872,6 +7898,7 @@ get()-blob builtin.txt /*get()-blob*
|
||||
get()-dict builtin.txt /*get()-dict*
|
||||
get()-func builtin.txt /*get()-func*
|
||||
get()-list builtin.txt /*get()-list*
|
||||
get()-tuple builtin.txt /*get()-tuple*
|
||||
get-ms-debuggers debug.txt /*get-ms-debuggers*
|
||||
getbufinfo() builtin.txt /*getbufinfo()*
|
||||
getbufline() builtin.txt /*getbufline()*
|
||||
@@ -8652,6 +8679,7 @@ list-modification eval.txt /*list-modification*
|
||||
list-repeat windows.txt /*list-repeat*
|
||||
list2blob() builtin.txt /*list2blob()*
|
||||
list2str() builtin.txt /*list2str()*
|
||||
list2tuple() builtin.txt /*list2tuple()*
|
||||
listener_add() builtin.txt /*listener_add()*
|
||||
listener_flush() builtin.txt /*listener_flush()*
|
||||
listener_remove() builtin.txt /*listener_remove()*
|
||||
@@ -10325,6 +10353,7 @@ subscript eval.txt /*subscript*
|
||||
substitute() builtin.txt /*substitute()*
|
||||
substitute-CR version6.txt /*substitute-CR*
|
||||
substring eval.txt /*substring*
|
||||
subtuple eval.txt /*subtuple*
|
||||
suffixes cmdline.txt /*suffixes*
|
||||
suspend starting.txt /*suspend*
|
||||
swap-exists-choices usr_11.txt /*swap-exists-choices*
|
||||
@@ -10574,6 +10603,7 @@ t_ti term.txt /*t_ti*
|
||||
t_tp version4.txt /*t_tp*
|
||||
t_ts term.txt /*t_ts*
|
||||
t_ts_old version4.txt /*t_ts_old*
|
||||
t_tuple-variable eval.txt /*t_tuple-variable*
|
||||
t_typealias-variable eval.txt /*t_typealias-variable*
|
||||
t_u7 term.txt /*t_u7*
|
||||
t_ue term.txt /*t_ue*
|
||||
@@ -10810,6 +10840,7 @@ test_null_job() testing.txt /*test_null_job()*
|
||||
test_null_list() testing.txt /*test_null_list()*
|
||||
test_null_partial() testing.txt /*test_null_partial()*
|
||||
test_null_string() testing.txt /*test_null_string()*
|
||||
test_null_tuple() testing.txt /*test_null_tuple()*
|
||||
test_option_not_set() testing.txt /*test_option_not_set()*
|
||||
test_override() testing.txt /*test_override()*
|
||||
test_refcount() testing.txt /*test_refcount()*
|
||||
@@ -10895,6 +10926,13 @@ try-echoerr eval.txt /*try-echoerr*
|
||||
try-finally eval.txt /*try-finally*
|
||||
try-nested eval.txt /*try-nested*
|
||||
try-nesting eval.txt /*try-nesting*
|
||||
tuple eval.txt /*tuple*
|
||||
tuple-concatenation eval.txt /*tuple-concatenation*
|
||||
tuple-functions usr_41.txt /*tuple-functions*
|
||||
tuple-identity eval.txt /*tuple-identity*
|
||||
tuple-index eval.txt /*tuple-index*
|
||||
tuple-type vim9.txt /*tuple-type*
|
||||
tuple2list() builtin.txt /*tuple2list()*
|
||||
tutor usr_01.txt /*tutor*
|
||||
two-engines pattern.txt /*two-engines*
|
||||
type() builtin.txt /*type()*
|
||||
@@ -11091,6 +11129,7 @@ v:t_none eval.txt /*v:t_none*
|
||||
v:t_number eval.txt /*v:t_number*
|
||||
v:t_object eval.txt /*v:t_object*
|
||||
v:t_string eval.txt /*v:t_string*
|
||||
v:t_tuple eval.txt /*v:t_tuple*
|
||||
v:t_typealias eval.txt /*v:t_typealias*
|
||||
v:termblinkresp eval.txt /*v:termblinkresp*
|
||||
v:termrbgresp eval.txt /*v:termrbgresp*
|
||||
@@ -11230,6 +11269,7 @@ variable-categories vim9.txt /*variable-categories*
|
||||
variable-scope eval.txt /*variable-scope*
|
||||
variable-types vim9.txt /*variable-types*
|
||||
variables eval.txt /*variables*
|
||||
variadic-tuple vim9.txt /*variadic-tuple*
|
||||
various various.txt /*various*
|
||||
various-cmds various.txt /*various-cmds*
|
||||
various-functions usr_41.txt /*various-functions*
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
*testing.txt* For Vim version 9.1. Last change: 2024 Jul 18
|
||||
*testing.txt* For Vim version 9.1. Last change: 2025 Mar 23
|
||||
|
||||
|
||||
VIM REFERENCE MANUAL by Bram Moolenaar
|
||||
@@ -364,6 +364,11 @@ test_null_string() *test_null_string()*
|
||||
|
||||
Return type: |String|
|
||||
|
||||
test_null_tuple() *test_null_tuple()*
|
||||
Return a |Tuple| that is null. Only useful for testing.
|
||||
|
||||
Return type: |Tuple|
|
||||
|
||||
test_option_not_set({name}) *test_option_not_set()*
|
||||
Reset the flag that indicates option {name} was set. Thus it
|
||||
looks like it still has the default value. Use like this: >
|
||||
|
||||
+45
-3
@@ -1,4 +1,4 @@
|
||||
*usr_41.txt* For Vim version 9.1. Last change: 2025 Feb 01
|
||||
*usr_41.txt* For Vim version 9.1. Last change: 2025 Mar 23
|
||||
|
||||
VIM USER MANUAL - by Bram Moolenaar
|
||||
|
||||
@@ -839,6 +839,30 @@ List manipulation: *list-functions*
|
||||
repeat() repeat a List multiple times
|
||||
flatten() flatten a List
|
||||
flattennew() flatten a copy of a List
|
||||
items() get List of List index-value pairs
|
||||
|
||||
Tuple manipulation: *tuple-functions*
|
||||
copy() make a shallow copy of a Tuple
|
||||
count() count number of times a value appears in a
|
||||
Tuple
|
||||
deepcopy() make a full copy of a Tuple
|
||||
empty() check if Tuple is empty
|
||||
foreach() apply function to Tuple items
|
||||
get() get an item without error for wrong index
|
||||
index() index of a value in a Tuple
|
||||
indexof() index in a Tuple where an expression is true
|
||||
items() get List of Tuple index-value pairs
|
||||
join() join Tuple items into a String
|
||||
len() number of items in a Tuple
|
||||
list2tuple() convert a list of items into a Tuple
|
||||
max() maximum value in a Tuple
|
||||
min() minimum value in a Tuple
|
||||
reduce() reduce a Tuple to a value
|
||||
repeat() repeat a Tuple multiple times
|
||||
reverse() reverse the order of items in a Tuple
|
||||
slice() take a slice of a Tuple
|
||||
string() string representation of a Tuple
|
||||
tuple2list() convert a Tuple of items into a list
|
||||
|
||||
Dictionary manipulation: *dict-functions*
|
||||
get() get an entry without an error for a wrong key
|
||||
@@ -1234,6 +1258,7 @@ Testing: *test-functions*
|
||||
test_null_list() return a null List
|
||||
test_null_partial() return a null Partial function
|
||||
test_null_string() return a null String
|
||||
test_null_tuple() return a null Tuple
|
||||
test_settime() set the time Vim uses internally
|
||||
test_setmouse() set the mouse position
|
||||
test_feedinput() add key sequence to input buffer
|
||||
@@ -1649,8 +1674,8 @@ More information about defining your own functions here: |user-functions|.
|
||||
==============================================================================
|
||||
*41.8* Lists and Dictionaries
|
||||
|
||||
So far we have used the basic types String and Number. Vim also supports two
|
||||
composite types: List and Dictionary.
|
||||
So far we have used the basic types String and Number. Vim also supports
|
||||
three composite types: List, Tuple and Dictionary.
|
||||
|
||||
A List is an ordered sequence of items. The items can be any kind of value,
|
||||
thus you can make a List of numbers, a List of Lists and even a List of mixed
|
||||
@@ -1751,6 +1776,23 @@ This looks into lines 1 to 50 (inclusive) and echoes any date found in there.
|
||||
|
||||
For further reading see |Lists|.
|
||||
|
||||
TUPLE
|
||||
|
||||
A Tuple is an immutable ordered sequence of items. An item can be of any
|
||||
type. Items can be accessed by their index number. To create a Tuple with
|
||||
three strings: >
|
||||
|
||||
var atuple = ('one', 'two', 'three')
|
||||
|
||||
The Tuple items are enclosed in parenthesis and separated by commas. To
|
||||
create an empty Tuple: >
|
||||
|
||||
var atuple = ()
|
||||
|
||||
The |:for| loop can be used to iterate over the items in a Tuple similar to a
|
||||
List.
|
||||
|
||||
For further reading see |Tuples|.
|
||||
|
||||
DICTIONARIES
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
*version9.txt* For Vim version 9.1. Last change: 2025 Mar 21
|
||||
*version9.txt* For Vim version 9.1. Last change: 2025 Mar 23
|
||||
|
||||
|
||||
VIM REFERENCE MANUAL by Bram Moolenaar
|
||||
@@ -41574,6 +41574,8 @@ Include the "linematch" algorithm for the 'diffopt' setting. This aligns
|
||||
changes between buffers on similar lines improving the diff highlighting in
|
||||
Vim
|
||||
|
||||
Support for the |Tuple| data type in Vim script and Vim9 script.
|
||||
|
||||
*changed-9.2*
|
||||
Changed~
|
||||
-------
|
||||
@@ -41677,11 +41679,14 @@ Functions: ~
|
||||
|getstacktrace()| get current stack trace of Vim scripts
|
||||
|id()| get unique identifier for a Dict, List, Object,
|
||||
Channel or Blob variable
|
||||
|list2tuple()| turn a List of items into a Tuple
|
||||
|matchbufline()| all the matches of a pattern in a buffer
|
||||
|matchstrlist()| all the matches of a pattern in a List of strings
|
||||
|ngettext()| lookup single/plural message translation
|
||||
|popup_setbuf()| switch to a different buffer in a popup
|
||||
|str2blob()| convert a List of strings into a blob
|
||||
|test_null_tuple()| return a null tuple
|
||||
|tuple2list()| turn a Tuple of items into a List
|
||||
|
||||
|
||||
Autocommands: ~
|
||||
|
||||
+38
-6
@@ -1,4 +1,4 @@
|
||||
*vim9.txt* For Vim version 9.1. Last change: 2025 Mar 06
|
||||
*vim9.txt* For Vim version 9.1. Last change: 2025 Mar 23
|
||||
|
||||
|
||||
VIM REFERENCE MANUAL by Bram Moolenaar
|
||||
@@ -1001,6 +1001,7 @@ empty list and dict is falsy:
|
||||
string non-empty
|
||||
blob non-empty
|
||||
list non-empty (different from JavaScript)
|
||||
tuple non-empty (different from JavaScript)
|
||||
dictionary non-empty (different from JavaScript)
|
||||
func when there is a function name
|
||||
special true or v:true
|
||||
@@ -1048,6 +1049,7 @@ In Vim9 script one can use the following predefined values: >
|
||||
null_function
|
||||
null_job
|
||||
null_list
|
||||
null_tuple
|
||||
null_object
|
||||
null_partial
|
||||
null_string
|
||||
@@ -1467,15 +1469,16 @@ The following builtin types are supported:
|
||||
dict<{type}>
|
||||
job
|
||||
channel
|
||||
tuple<{type}>
|
||||
tuple<{type}, {type}, ...>
|
||||
tuple<...list<{type}>>
|
||||
tuple<{type}, ...list<{type}>>
|
||||
func
|
||||
func: {type}
|
||||
func({type}, ...)
|
||||
func({type}, ...): {type}
|
||||
void
|
||||
|
||||
Not supported yet:
|
||||
tuple<a: {type}, b: {type}, ...>
|
||||
|
||||
These types can be used in declarations, but no simple value will actually
|
||||
have the "void" type. Trying to use a void (e.g. a function without a
|
||||
return value) results in error *E1031* *E1186* .
|
||||
@@ -1483,6 +1486,32 @@ return value) results in error *E1031* *E1186* .
|
||||
There is no array type, use list<{type}> instead. For a list constant an
|
||||
efficient implementation is used that avoids allocating a lot of small pieces
|
||||
of memory.
|
||||
*tuple-type*
|
||||
A tuple type can be declared in more or less specific ways:
|
||||
tuple<number> a tuple with a single item of type |Number|
|
||||
tuple<number, string> a tuple with two items of type |Number| and
|
||||
|String|
|
||||
tuple<number, float, bool> a tuple with three items of type |Number|,
|
||||
|Float| and |Boolean|.
|
||||
tuple<...list<number>> a variadic tuple with zero or more items of
|
||||
type |Number|.
|
||||
tuple<number, ...list<string>> a tuple with an item of type |Number| followed
|
||||
by zero or more items of type |String|.
|
||||
|
||||
Examples: >
|
||||
var myTuple: tuple<number> = (20,)
|
||||
var myTuple: tuple<number, string> = (30, 'vim')
|
||||
var myTuple: tuple<number, float, bool> = (40, 1.1, true)
|
||||
var myTuple: tuple<...list<string>> = ('a', 'b', 'c')
|
||||
var myTuple: tuple<number, ...list<string>> = (3, 'a', 'b', 'c')
|
||||
<
|
||||
*variadic-tuple* *E1539*
|
||||
A variadic tuple has zero or more items of the same type. The type of a
|
||||
variadic tuple must end with a list type. Examples: >
|
||||
var myTuple: tuple<...list<number>> = (1, 2, 3)
|
||||
var myTuple: tuple<...list<string>> = ('a', 'b', 'c')
|
||||
var myTuple: tuple<...list<bool>> = ()
|
||||
<
|
||||
*vim9-func-declaration* *E1005* *E1007*
|
||||
A partial and function can be declared in more or less specific ways:
|
||||
func any kind of function reference, no type
|
||||
@@ -1707,7 +1736,8 @@ argument type checking: >
|
||||
*E1211* *E1217* *E1218* *E1219* *E1220* *E1221*
|
||||
*E1222* *E1223* *E1224* *E1225* *E1226* *E1227*
|
||||
*E1228* *E1238* *E1250* *E1251* *E1252* *E1256*
|
||||
*E1297* *E1298* *E1301*
|
||||
*E1297* *E1298* *E1301* *E1528* *E1529* *E1530*
|
||||
*E1531* *E1534*
|
||||
Types are checked for most builtin functions to make it easier to spot
|
||||
mistakes.
|
||||
|
||||
@@ -1715,7 +1745,7 @@ Categories of variables, defaults and null handling ~
|
||||
*variable-categories* *null-variables*
|
||||
There are categories of variables:
|
||||
primitive number, float, boolean
|
||||
container string, blob, list, dict
|
||||
container string, blob, list, tuple, dict
|
||||
specialized function, job, channel, user-defined-object
|
||||
|
||||
When declaring a variable without an initializer, an explicit type must be
|
||||
@@ -1845,6 +1875,7 @@ An uninitialized variable is usually equal to null; it depends on its type:
|
||||
var s: string s == null
|
||||
var b: blob b != null ***
|
||||
var l: list<any> l != null ***
|
||||
var t: tuple<any> t != null ***
|
||||
var d: dict<any> d != null ***
|
||||
var f: func f == null
|
||||
var j: job j == null
|
||||
@@ -1855,6 +1886,7 @@ A variable initialized to empty equals null_<type>; but not null:
|
||||
var s2: string = "" == null_string != null
|
||||
var b2: blob = 0z == null_blob != null
|
||||
var l2: list<any> = [] == null_list != null
|
||||
var t2: tuple<any> = () == null_tuple != null
|
||||
var d2: dict<any> = {} == null_dict != null
|
||||
|
||||
NOTE: the specialized variables, like job, default to null value and have no
|
||||
|
||||
@@ -169,6 +169,7 @@ SRC += \
|
||||
textobject.c \
|
||||
textprop.c \
|
||||
time.c \
|
||||
tuple.c \
|
||||
typval.c \
|
||||
ui.c \
|
||||
undo.c \
|
||||
|
||||
@@ -865,6 +865,7 @@ OBJ = \
|
||||
$(OUTDIR)/textobject.o \
|
||||
$(OUTDIR)/textprop.o \
|
||||
$(OUTDIR)/time.o \
|
||||
$(OUTDIR)/tuple.o \
|
||||
$(OUTDIR)/typval.o \
|
||||
$(OUTDIR)/ui.o \
|
||||
$(OUTDIR)/undo.o \
|
||||
|
||||
@@ -786,6 +786,7 @@ OBJ = \
|
||||
$(OUTDIR)\textobject.obj \
|
||||
$(OUTDIR)\textprop.obj \
|
||||
$(OUTDIR)\time.obj \
|
||||
$(OUTDIR)\tuple.obj \
|
||||
$(OUTDIR)\typval.obj \
|
||||
$(OUTDIR)\ui.obj \
|
||||
$(OUTDIR)\undo.obj \
|
||||
@@ -1791,6 +1792,8 @@ $(OUTDIR)/textprop.obj: $(OUTDIR) textprop.c $(INCL)
|
||||
|
||||
$(OUTDIR)/time.obj: $(OUTDIR) time.c $(INCL)
|
||||
|
||||
$(OUTDIR)/tuple.obj: $(OUTDIR) tuple.c $(INCL)
|
||||
|
||||
$(OUTDIR)/typval.obj: $(OUTDIR) typval.c $(INCL)
|
||||
|
||||
$(OUTDIR)/ui.obj: $(OUTDIR) ui.c $(INCL)
|
||||
@@ -2005,6 +2008,7 @@ proto.h: \
|
||||
proto/textobject.pro \
|
||||
proto/textprop.pro \
|
||||
proto/time.pro \
|
||||
proto/tuple.pro \
|
||||
proto/typval.pro \
|
||||
proto/ui.pro \
|
||||
proto/undo.pro \
|
||||
|
||||
@@ -433,6 +433,7 @@ SRC = \
|
||||
textobject.c \
|
||||
textprop.c \
|
||||
time.c \
|
||||
tuple.c \
|
||||
typval.c \
|
||||
ui.c \
|
||||
undo.c \
|
||||
@@ -567,6 +568,7 @@ OBJ = \
|
||||
textobject.obj \
|
||||
textprop.obj \
|
||||
time.obj \
|
||||
tuple.obj \
|
||||
typval.obj \
|
||||
ui.obj \
|
||||
undo.obj \
|
||||
@@ -1169,6 +1171,9 @@ textprop.obj : textprop.c vim.h [.auto]config.h feature.h os_unix.h \
|
||||
time.obj : time.c vim.h [.auto]config.h feature.h os_unix.h \
|
||||
ascii.h keymap.h termdefs.h macros.h structs.h regexp.h gui.h beval.h \
|
||||
[.proto]gui_beval.pro option.h ex_cmds.h proto.h errors.h globals.h
|
||||
tuple.obj : tuple.c vim.h [.auto]config.h feature.h os_unix.h \
|
||||
ascii.h keymap.h termdefs.h macros.h structs.h regexp.h gui.h beval.h \
|
||||
[.proto]gui_beval.pro option.h ex_cmds.h proto.h errors.h globals.h
|
||||
typval.obj : typval.c vim.h [.auto]config.h feature.h os_unix.h \
|
||||
ascii.h keymap.h termdefs.h macros.h structs.h regexp.h gui.h beval.h \
|
||||
[.proto]gui_beval.pro option.h ex_cmds.h proto.h errors.h globals.h
|
||||
|
||||
@@ -1584,6 +1584,7 @@ BASIC_SRC = \
|
||||
textobject.c \
|
||||
textprop.c \
|
||||
time.c \
|
||||
tuple.c \
|
||||
typval.c \
|
||||
ui.c \
|
||||
undo.c \
|
||||
@@ -1744,6 +1745,7 @@ OBJ_COMMON = \
|
||||
objects/textobject.o \
|
||||
objects/textprop.o \
|
||||
objects/time.o \
|
||||
objects/tuple.o \
|
||||
objects/typval.o \
|
||||
objects/ui.o \
|
||||
objects/undo.o \
|
||||
@@ -1937,6 +1939,7 @@ PRO_AUTO = \
|
||||
textobject.pro \
|
||||
textprop.pro \
|
||||
time.pro \
|
||||
tuple.pro \
|
||||
typval.pro \
|
||||
ui.pro \
|
||||
undo.pro \
|
||||
@@ -3568,6 +3571,9 @@ objects/textprop.o: textprop.c
|
||||
objects/time.o: time.c
|
||||
$(CCC) -o $@ time.c
|
||||
|
||||
objects/tuple.o: tuple.c
|
||||
$(CCC) -o $@ tuple.c
|
||||
|
||||
objects/typval.o: typval.c
|
||||
$(CCC) -o $@ typval.c
|
||||
|
||||
@@ -4248,6 +4254,11 @@ objects/time.o: time.c vim.h protodef.h auto/config.h feature.h os_unix.h \
|
||||
proto/gui_beval.pro structs.h regexp.h gui.h libvterm/include/vterm.h \
|
||||
libvterm/include/vterm_keycodes.h alloc.h ex_cmds.h spell.h proto.h \
|
||||
globals.h errors.h
|
||||
objects/tuple.o: tuple.c vim.h protodef.h auto/config.h feature.h os_unix.h \
|
||||
auto/osdef.h ascii.h keymap.h termdefs.h macros.h option.h beval.h \
|
||||
proto/gui_beval.pro structs.h regexp.h gui.h libvterm/include/vterm.h \
|
||||
libvterm/include/vterm_keycodes.h alloc.h ex_cmds.h spell.h proto.h \
|
||||
globals.h errors.h
|
||||
objects/typval.o: typval.c vim.h protodef.h auto/config.h feature.h os_unix.h \
|
||||
auto/osdef.h ascii.h keymap.h termdefs.h macros.h option.h beval.h \
|
||||
proto/gui_beval.pro structs.h regexp.h gui.h libvterm/include/vterm.h \
|
||||
|
||||
+1
-1
@@ -5004,7 +5004,7 @@ set_ref_in_channel(int copyID)
|
||||
{
|
||||
tv.v_type = VAR_CHANNEL;
|
||||
tv.vval.v_channel = channel;
|
||||
abort = abort || set_ref_in_item(&tv, copyID, NULL, NULL);
|
||||
abort = abort || set_ref_in_item(&tv, copyID, NULL, NULL, NULL);
|
||||
}
|
||||
return abort;
|
||||
}
|
||||
|
||||
+3
-1
@@ -1554,7 +1554,7 @@ dict2list(typval_T *argvars, typval_T *rettv, dict2list_T what)
|
||||
return;
|
||||
|
||||
if ((what == DICT2LIST_ITEMS
|
||||
? check_for_string_or_list_or_dict_arg(argvars, 0)
|
||||
? check_for_string_list_tuple_or_dict_arg(argvars, 0)
|
||||
: check_for_dict_arg(argvars, 0)) == FAIL)
|
||||
return;
|
||||
|
||||
@@ -1617,6 +1617,8 @@ f_items(typval_T *argvars, typval_T *rettv)
|
||||
string2items(argvars, rettv);
|
||||
else if (argvars[0].v_type == VAR_LIST)
|
||||
list2items(argvars, rettv);
|
||||
else if (argvars[0].v_type == VAR_TUPLE)
|
||||
tuple2items(argvars, rettv);
|
||||
else
|
||||
dict2list(argvars, rettv, DICT2LIST_ITEMS);
|
||||
}
|
||||
|
||||
+60
-8
@@ -2932,8 +2932,8 @@ EXTERN char e_using_bool_as_number[]
|
||||
INIT(= N_("E1138: Using a Bool as a Number"));
|
||||
EXTERN char e_missing_matching_bracket_after_dict_key[]
|
||||
INIT(= N_("E1139: Missing matching bracket after dict key"));
|
||||
EXTERN char e_for_argument_must_be_sequence_of_lists[]
|
||||
INIT(= N_("E1140: :for argument must be a sequence of lists"));
|
||||
EXTERN char e_for_argument_must_be_sequence_of_lists_or_tuples[]
|
||||
INIT(= N_("E1140: :for argument must be a sequence of lists or tuples"));
|
||||
EXTERN char e_indexable_type_required[]
|
||||
INIT(= N_("E1141: Indexable type required"));
|
||||
EXTERN char e_calling_test_garbagecollect_now_while_v_testing_is_not_set[]
|
||||
@@ -3146,8 +3146,8 @@ EXTERN char e_string_or_dict_required_for_argument_nr[]
|
||||
INIT(= N_("E1223: String or Dictionary required for argument %d"));
|
||||
EXTERN char e_string_number_or_list_required_for_argument_nr[]
|
||||
INIT(= N_("E1224: String, Number or List required for argument %d"));
|
||||
EXTERN char e_string_list_or_dict_required_for_argument_nr[]
|
||||
INIT(= N_("E1225: String, List or Dictionary required for argument %d"));
|
||||
EXTERN char e_string_list_tuple_or_dict_required_for_argument_nr[]
|
||||
INIT(= N_("E1225: String, List, Tuple or Dictionary required for argument %d"));
|
||||
EXTERN char e_list_or_blob_required_for_argument_nr[]
|
||||
INIT(= N_("E1226: List or Blob required for argument %d"));
|
||||
EXTERN char e_list_or_dict_required_for_argument_nr[]
|
||||
@@ -3218,10 +3218,12 @@ EXTERN char e_highlight_group_name_too_long[]
|
||||
#ifdef FEAT_EVAL
|
||||
EXTERN char e_argument_of_str_must_be_list_string_dictionary_or_blob[]
|
||||
INIT(= N_("E1250: Argument of %s must be a List, String, Dictionary or Blob"));
|
||||
EXTERN char e_list_dict_blob_or_string_required_for_argument_nr[]
|
||||
INIT(= N_("E1251: List, Dictionary, Blob or String required for argument %d"));
|
||||
EXTERN char e_list_tuple_dict_blob_or_string_required_for_argument_nr[]
|
||||
INIT(= N_("E1251: List, Tuple, Dictionary, Blob or String required for argument %d"));
|
||||
EXTERN char e_string_list_or_blob_required_for_argument_nr[]
|
||||
INIT(= N_("E1252: String, List or Blob required for argument %d"));
|
||||
EXTERN char e_string_list_tuple_or_blob_required_for_argument_nr[]
|
||||
INIT(= N_("E1253: String, List, Tuple or Blob required for argument %d"));
|
||||
// E1253 unused
|
||||
EXTERN char e_cannot_use_script_variable_in_for_loop[]
|
||||
INIT(= N_("E1254: Cannot use script variable in for loop"));
|
||||
@@ -3351,8 +3353,8 @@ EXTERN char e_window_unexpectedly_close_while_searching_for_tags[]
|
||||
#ifdef FEAT_EVAL
|
||||
EXTERN char e_cannot_use_partial_with_dictionary_for_defer[]
|
||||
INIT(= N_("E1300: Cannot use a partial with dictionary for :defer"));
|
||||
EXTERN char e_string_number_list_or_blob_required_for_argument_nr[]
|
||||
INIT(= N_("E1301: String, Number, List or Blob required for argument %d"));
|
||||
EXTERN char e_repeatable_type_required_for_argument_nr[]
|
||||
INIT(= N_("E1301: String, Number, List, Tuple or Blob required for argument %d"));
|
||||
EXTERN char e_script_variable_was_deleted[]
|
||||
INIT(= N_("E1302: Script variable was deleted"));
|
||||
EXTERN char e_custom_list_completion_function_does_not_return_list_but_str[]
|
||||
@@ -3664,3 +3666,53 @@ EXTERN char e_str_encoding_from_failed[]
|
||||
INIT(= N_("E1515: Unable to convert from '%s' encoding"));
|
||||
EXTERN char e_str_encoding_to_failed[]
|
||||
INIT(= N_("E1516: Unable to convert to '%s' encoding"));
|
||||
#ifdef FEAT_EVAL
|
||||
EXTERN char e_can_only_compare_tuple_with_tuple[]
|
||||
INIT(= N_("E1517: Can only compare Tuple with Tuple"));
|
||||
EXTERN char e_invalid_operation_for_tuple[]
|
||||
INIT(= N_("E1518: Invalid operation for Tuple"));
|
||||
EXTERN char e_tuple_index_out_of_range_nr[]
|
||||
INIT(= N_("E1519: Tuple index out of range: %ld"));
|
||||
EXTERN char e_using_tuple_as_number[]
|
||||
INIT(= N_("E1520: Using a Tuple as a Number"));
|
||||
EXTERN char e_using_tuple_as_float[]
|
||||
INIT(= N_("E1521: Using a Tuple as a Float"));
|
||||
EXTERN char e_using_tuple_as_string[]
|
||||
INIT(= N_("E1522: Using a Tuple as a String"));
|
||||
EXTERN char e_string_list_tuple_or_blob_required[]
|
||||
INIT(= N_("E1523: String, List, Tuple or Blob required"));
|
||||
EXTERN char e_cannot_use_tuple_with_function_str[]
|
||||
INIT(= N_("E1524: Cannot use a tuple with function %s"));
|
||||
EXTERN char e_argument_of_str_must_be_list_tuple_string_dictionary_or_blob[]
|
||||
INIT(= N_("E1525: Argument of %s must be a List, Tuple, String, Dictionary or Blob"));
|
||||
EXTERN char e_missing_end_of_tuple_rsp_str[]
|
||||
INIT(= N_("E1526: Missing end of Tuple ')': %s"));
|
||||
EXTERN char e_missing_comma_in_tuple_str[]
|
||||
INIT(= N_("E1527: Missing comma in Tuple: %s"));
|
||||
EXTERN char e_list_or_tuple_or_blob_required_for_argument_nr[]
|
||||
INIT(= N_("E1528: List or Tuple or Blob required for argument %d"));
|
||||
EXTERN char e_list_or_tuple_required_for_argument_nr[]
|
||||
INIT(= N_("E1529: List or Tuple required for argument %d"));
|
||||
EXTERN char e_list_or_tuple_or_dict_required_for_argument_nr[]
|
||||
INIT(= N_("E1530: List or Tuple or Dictionary required for argument %d"));
|
||||
EXTERN char e_argument_of_str_must_be_list_tuple_dictionary_or_blob[]
|
||||
INIT(= N_("E1531: Argument of %s must be a List, Tuple, Dictionary or Blob"));
|
||||
EXTERN char e_tuple_is_immutable[]
|
||||
INIT(= N_("E1532: Cannot modify a tuple"));
|
||||
EXTERN char e_cannot_slice_tuple[]
|
||||
INIT(= N_("E1533: Cannot slice a tuple"));
|
||||
EXTERN char e_tuple_required_for_argument_nr[]
|
||||
INIT(= N_("E1534: Tuple required for argument %d"));
|
||||
EXTERN char e_list_or_tuple_required[]
|
||||
INIT(= N_("E1535: List or Tuple required"));
|
||||
EXTERN char e_tuple_required[]
|
||||
INIT(= N_("E1536: Tuple required"));
|
||||
EXTERN char e_less_targets_than_tuple_items[]
|
||||
INIT(= N_("E1537: Less targets than Tuple items"));
|
||||
EXTERN char e_more_targets_than_tuple_items[]
|
||||
INIT(= N_("E1538: More targets than Tuple items"));
|
||||
EXTERN char e_variadic_tuple_must_end_with_list_type_str[]
|
||||
INIT(= N_("E1539: Variadic tuple must end with a list type: %s"));
|
||||
EXTERN char e_cannot_use_variadic_tuple_in_concatenation[]
|
||||
INIT(= N_("E1540: Cannot use a variadic tuple in concatenation"));
|
||||
#endif
|
||||
|
||||
+325
-37
@@ -107,7 +107,7 @@ eval_clear(void)
|
||||
// autoloaded script names
|
||||
free_autoload_scriptnames();
|
||||
|
||||
// unreferenced lists and dicts
|
||||
// unreferenced lists, tuples and dicts
|
||||
(void)garbage_collect(FALSE);
|
||||
|
||||
// functions not garbage collected
|
||||
@@ -620,28 +620,48 @@ skip_expr_concatenate(
|
||||
|
||||
/*
|
||||
* Convert "tv" to a string.
|
||||
* When "join_list" is TRUE convert a List into a sequence of lines.
|
||||
* When "join_list" is TRUE convert a List or a Tuple into a sequence of lines.
|
||||
* Returns an allocated string (NULL when out of memory).
|
||||
*/
|
||||
char_u *
|
||||
typval2string(typval_T *tv, int join_list)
|
||||
{
|
||||
garray_T ga;
|
||||
char_u *retval;
|
||||
char_u *retval = NULL;
|
||||
|
||||
if (join_list && tv->v_type == VAR_LIST)
|
||||
if (join_list && (tv->v_type == VAR_LIST || tv->v_type == VAR_TUPLE))
|
||||
{
|
||||
ga_init2(&ga, sizeof(char), 80);
|
||||
if (tv->vval.v_list != NULL)
|
||||
if (tv->v_type == VAR_LIST)
|
||||
{
|
||||
list_join(&ga, tv->vval.v_list, (char_u *)"\n", TRUE, FALSE, 0);
|
||||
if (tv->vval.v_list->lv_len > 0)
|
||||
ga_append(&ga, NL);
|
||||
ga_init2(&ga, sizeof(char), 80);
|
||||
if (tv->vval.v_list != NULL)
|
||||
{
|
||||
list_join(&ga, tv->vval.v_list, (char_u *)"\n", TRUE, FALSE,
|
||||
0);
|
||||
if (tv->vval.v_list->lv_len > 0)
|
||||
ga_append(&ga, NL);
|
||||
}
|
||||
ga_append(&ga, NUL);
|
||||
retval = (char_u *)ga.ga_data;
|
||||
}
|
||||
else
|
||||
{
|
||||
// tuple
|
||||
ga_init2(&ga, sizeof(char), 80);
|
||||
if (tv->vval.v_tuple != NULL)
|
||||
{
|
||||
tuple_join(&ga, tv->vval.v_tuple, (char_u *)"\n", TRUE, FALSE,
|
||||
0);
|
||||
if (TUPLE_LEN(tv->vval.v_tuple) > 0)
|
||||
ga_append(&ga, NL);
|
||||
}
|
||||
ga_append(&ga, NUL);
|
||||
retval = (char_u *)ga.ga_data;
|
||||
}
|
||||
ga_append(&ga, NUL);
|
||||
retval = (char_u *)ga.ga_data;
|
||||
}
|
||||
else if (tv->v_type == VAR_LIST || tv->v_type == VAR_DICT)
|
||||
else if (tv->v_type == VAR_LIST
|
||||
|| tv->v_type == VAR_TUPLE
|
||||
|| tv->v_type == VAR_DICT)
|
||||
{
|
||||
char_u *tofree;
|
||||
char_u numbuf[NUMBUFLEN];
|
||||
@@ -659,7 +679,8 @@ typval2string(typval_T *tv, int join_list)
|
||||
/*
|
||||
* Top level evaluation function, returning a string. Does not handle line
|
||||
* breaks.
|
||||
* When "join_list" is TRUE convert a List into a sequence of lines.
|
||||
* When "join_list" is TRUE convert a List and a Tuple into a sequence of
|
||||
* lines.
|
||||
* Return pointer to allocated memory, or NULL for failure.
|
||||
*/
|
||||
char_u *
|
||||
@@ -1095,7 +1116,7 @@ flag_string_T glv_flag_strings[] = {
|
||||
*
|
||||
* This is typically called with "lval_root" as "root". For a class, find
|
||||
* the name from lp in the class from root, fill in lval_T if found. For a
|
||||
* complex type, list/dict use it as the result; just put the root into
|
||||
* complex type, list/tuple/dict use it as the result; just put the root into
|
||||
* ll_tv.
|
||||
*
|
||||
* "lval_root" is a hack used during run-time/instr-execution to provide the
|
||||
@@ -1322,8 +1343,11 @@ get_lval_dict_item(
|
||||
return GLV_FAIL;
|
||||
}
|
||||
lp->ll_list = NULL;
|
||||
lp->ll_list = NULL;
|
||||
lp->ll_blob = NULL;
|
||||
lp->ll_object = NULL;
|
||||
lp->ll_class = NULL;
|
||||
lp->ll_tuple = NULL;
|
||||
|
||||
// a NULL dict is equivalent with an empty dict
|
||||
if (lp->ll_tv->vval.v_dict == NULL)
|
||||
@@ -1425,7 +1449,13 @@ get_lval_blob(
|
||||
{
|
||||
long bloblen = blob_len(lp->ll_tv->vval.v_blob);
|
||||
|
||||
// Get the number and item for the only or first index of the List.
|
||||
lp->ll_list = NULL;
|
||||
lp->ll_dict = NULL;
|
||||
lp->ll_object = NULL;
|
||||
lp->ll_class = NULL;
|
||||
lp->ll_tuple = NULL;
|
||||
|
||||
// Get the number and item for the only or first index of a List or Tuple.
|
||||
if (empty1)
|
||||
lp->ll_n1 = 0;
|
||||
else
|
||||
@@ -1484,6 +1514,7 @@ get_lval_list(
|
||||
lp->ll_dict = NULL;
|
||||
lp->ll_object = NULL;
|
||||
lp->ll_class = NULL;
|
||||
lp->ll_tuple = NULL;
|
||||
lp->ll_list = lp->ll_tv->vval.v_list;
|
||||
lp->ll_li = check_range_index_one(lp->ll_list, &lp->ll_n1,
|
||||
(flags & GLV_ASSIGN_WITH_OP) == 0, quiet);
|
||||
@@ -1523,6 +1554,64 @@ get_lval_list(
|
||||
return OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get a tuple lval variable that can be assigned a value to: "name",
|
||||
* "na{me}", "name[expr]", "name[expr][expr]", etc.
|
||||
*
|
||||
* 'idx' specifies the tuple index.
|
||||
* If 'quiet' is TRUE, then error messages are not displayed for an invalid
|
||||
* index.
|
||||
*
|
||||
* The typval is returned in 'lp'. Returns GLV_OK on success and GLV_FAIL on
|
||||
* failure.
|
||||
*/
|
||||
static int
|
||||
get_lval_tuple(
|
||||
lval_T *lp,
|
||||
typval_T *idx,
|
||||
int quiet)
|
||||
{
|
||||
// is number or string
|
||||
lp->ll_n1 = (long)tv_get_number(idx);
|
||||
|
||||
lp->ll_list = NULL;
|
||||
lp->ll_dict = NULL;
|
||||
lp->ll_blob = NULL;
|
||||
lp->ll_object = NULL;
|
||||
lp->ll_class = NULL;
|
||||
|
||||
lp->ll_tuple = lp->ll_tv->vval.v_tuple;
|
||||
lp->ll_tv = tuple_find(lp->ll_tuple, lp->ll_n1);
|
||||
if (lp->ll_tv == NULL)
|
||||
{
|
||||
if (!quiet)
|
||||
semsg(_(e_tuple_index_out_of_range_nr), lp->ll_n1);
|
||||
return GLV_FAIL;
|
||||
}
|
||||
|
||||
// use the type of the member
|
||||
if (lp->ll_valtype != NULL)
|
||||
{
|
||||
if (lp->ll_valtype != NULL
|
||||
&& lp->ll_valtype->tt_type == VAR_TUPLE
|
||||
&& lp->ll_valtype->tt_argcount == 1)
|
||||
{
|
||||
// a variadic tuple or a single item tuple
|
||||
if (lp->ll_valtype->tt_flags & TTFLAG_VARARGS)
|
||||
lp->ll_valtype = lp->ll_valtype->tt_args[0]->tt_member;
|
||||
else
|
||||
lp->ll_valtype = lp->ll_valtype->tt_args[0];
|
||||
}
|
||||
else
|
||||
// If the LHS member type is not known (VAR_ANY), then get it from
|
||||
// the tuple item (after indexing)
|
||||
lp->ll_valtype = typval2type(lp->ll_tv, get_copyID(),
|
||||
&lp->ll_type_list, TVTT_DO_MEMBER);
|
||||
}
|
||||
|
||||
return GLV_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get a class or object lval method in class "cl". The 'key' argument points
|
||||
* to the method name and 'key_end' points to the character after 'key'.
|
||||
@@ -1630,6 +1719,7 @@ get_lval_class_or_obj(
|
||||
{
|
||||
lp->ll_dict = NULL;
|
||||
lp->ll_list = NULL;
|
||||
lp->ll_tuple = NULL;
|
||||
|
||||
class_T *cl;
|
||||
if (v_type == VAR_OBJECT)
|
||||
@@ -1697,8 +1787,8 @@ dot_allowed_after_type(char_u *name, vartype_T v_type, int quiet)
|
||||
|
||||
/*
|
||||
* Check whether left bracket ("[") is allowed after the variable "name" with
|
||||
* type "v_type". Only Dict, List and Blob types support a bracket after the
|
||||
* variable name. Returns TRUE if bracket is allowed after the name.
|
||||
* type "v_type". Only Dict, List, Tuple and Blob types support a bracket
|
||||
* after the variable name. Returns TRUE if bracket is allowed after the name.
|
||||
*/
|
||||
static int
|
||||
bracket_allowed_after_type(char_u *name, vartype_T v_type, int quiet)
|
||||
@@ -1716,14 +1806,18 @@ bracket_allowed_after_type(char_u *name, vartype_T v_type, int quiet)
|
||||
|
||||
/*
|
||||
* Check whether the variable "name" with type "v_type" can be followed by an
|
||||
* index. Only Dict, List, Blob, Object and Class types support indexing.
|
||||
* Returns TRUE if indexing is allowed after the name.
|
||||
* index. Only Dict, List, Tuple, Blob, Object and Class types support
|
||||
* indexing. Returns TRUE if indexing is allowed after the name.
|
||||
*/
|
||||
static int
|
||||
index_allowed_after_type(char_u *name, vartype_T v_type, int quiet)
|
||||
{
|
||||
if (v_type != VAR_LIST && v_type != VAR_DICT && v_type != VAR_BLOB &&
|
||||
v_type != VAR_OBJECT && v_type != VAR_CLASS)
|
||||
if (v_type != VAR_LIST
|
||||
&& v_type != VAR_TUPLE
|
||||
&& v_type != VAR_DICT
|
||||
&& v_type != VAR_BLOB
|
||||
&& v_type != VAR_OBJECT
|
||||
&& v_type != VAR_CLASS)
|
||||
{
|
||||
if (!quiet)
|
||||
semsg(_(e_index_not_allowed_after_str_str),
|
||||
@@ -1735,8 +1829,8 @@ index_allowed_after_type(char_u *name, vartype_T v_type, int quiet)
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the lval of a list/dict/blob/object/class subitem starting at "p". Loop
|
||||
* until no more [idx] or .key is following.
|
||||
* Get the lval of a list/tuple/dict/blob/object/class subitem starting at "p".
|
||||
* Loop until no more [idx] or .key is following.
|
||||
*
|
||||
* If "rettv" is not NULL it points to the value to be assigned.
|
||||
* "unlet" is TRUE for ":unlet".
|
||||
@@ -1863,6 +1957,12 @@ get_lval_subscript(
|
||||
emsg(_(e_cannot_slice_dictionary));
|
||||
goto done;
|
||||
}
|
||||
if (v_type == VAR_TUPLE)
|
||||
{
|
||||
if (!quiet)
|
||||
emsg(_(e_cannot_slice_tuple));
|
||||
goto done;
|
||||
}
|
||||
if (rettv != NULL
|
||||
&& !(rettv->v_type == VAR_LIST
|
||||
&& rettv->vval.v_list != NULL)
|
||||
@@ -1932,6 +2032,11 @@ get_lval_subscript(
|
||||
if (get_lval_list(lp, &var1, &var2, empty1, flags, quiet) == FAIL)
|
||||
goto done;
|
||||
}
|
||||
else if (v_type == VAR_TUPLE)
|
||||
{
|
||||
if (get_lval_tuple(lp, &var1, quiet) == FAIL)
|
||||
goto done;
|
||||
}
|
||||
else // v_type == VAR_CLASS || v_type == VAR_OBJECT
|
||||
{
|
||||
if (get_lval_class_or_obj(lp, key, p, v_type, cl_exec, flags,
|
||||
@@ -1945,6 +2050,13 @@ get_lval_subscript(
|
||||
var2.v_type = VAR_UNKNOWN;
|
||||
}
|
||||
|
||||
if (lp->ll_tuple != NULL)
|
||||
{
|
||||
if (!quiet)
|
||||
emsg(_(e_tuple_is_immutable));
|
||||
goto done;
|
||||
}
|
||||
|
||||
rc = OK;
|
||||
|
||||
done:
|
||||
@@ -2575,6 +2687,7 @@ tv_op(typval_T *tv1, typval_T *tv2, char_u *op)
|
||||
case VAR_OBJECT:
|
||||
case VAR_CLASS:
|
||||
case VAR_TYPEALIAS:
|
||||
case VAR_TUPLE:
|
||||
break;
|
||||
|
||||
case VAR_BLOB:
|
||||
@@ -2619,6 +2732,7 @@ eval_for_line(
|
||||
char_u *expr;
|
||||
typval_T tv;
|
||||
list_T *l;
|
||||
tuple_T *tuple;
|
||||
int skip = !(evalarg->eval_flags & EVAL_EVALUATE);
|
||||
|
||||
*errp = TRUE; // default: there is an error
|
||||
@@ -2671,6 +2785,22 @@ eval_for_line(
|
||||
fi->fi_lw.lw_item = l->lv_first;
|
||||
}
|
||||
}
|
||||
else if (tv.v_type == VAR_TUPLE)
|
||||
{
|
||||
tuple = tv.vval.v_tuple;
|
||||
if (tuple == NULL)
|
||||
{
|
||||
// a null tuple is like an empty tuple: do nothing
|
||||
clear_tv(&tv);
|
||||
}
|
||||
else
|
||||
{
|
||||
// No need to increment the refcount, it's already set for
|
||||
// the tuple being used in "tv".
|
||||
fi->fi_tuple = tuple;
|
||||
fi->fi_tuple_idx = 0;
|
||||
}
|
||||
}
|
||||
else if (tv.v_type == VAR_BLOB)
|
||||
{
|
||||
fi->fi_bi = 0;
|
||||
@@ -2695,7 +2825,7 @@ eval_for_line(
|
||||
}
|
||||
else
|
||||
{
|
||||
emsg(_(e_string_list_or_blob_required));
|
||||
emsg(_(e_string_list_tuple_or_blob_required));
|
||||
clear_tv(&tv);
|
||||
}
|
||||
}
|
||||
@@ -2780,6 +2910,22 @@ next_for_item(void *fi_void, char_u *arg)
|
||||
return result;
|
||||
}
|
||||
|
||||
if (fi->fi_tuple != NULL)
|
||||
{
|
||||
typval_T tv;
|
||||
|
||||
if (fi->fi_tuple_idx >= TUPLE_LEN(fi->fi_tuple))
|
||||
return FALSE;
|
||||
|
||||
copy_tv(TUPLE_ITEM(fi->fi_tuple, fi->fi_tuple_idx), &tv);
|
||||
++fi->fi_tuple_idx;
|
||||
++fi->fi_bi;
|
||||
if (skip_assign)
|
||||
return TRUE;
|
||||
return ex_let_vars(arg, &tv, TRUE, fi->fi_semicolon,
|
||||
fi->fi_varcount, flag, NULL) == OK;
|
||||
}
|
||||
|
||||
item = fi->fi_lw.lw_item;
|
||||
if (item == NULL)
|
||||
result = FALSE;
|
||||
@@ -2813,6 +2959,8 @@ free_for_info(void *fi_void)
|
||||
}
|
||||
else if (fi->fi_blob != NULL)
|
||||
blob_unref(fi->fi_blob);
|
||||
else if (fi->fi_tuple != NULL)
|
||||
tuple_unref(fi->fi_tuple);
|
||||
else
|
||||
vim_free(fi->fi_string);
|
||||
vim_free(fi);
|
||||
@@ -3959,6 +4107,36 @@ eval_addlist(typval_T *tv1, typval_T *tv2)
|
||||
return OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* Make a copy of tuple "tv1" and append tuple "tv2".
|
||||
*/
|
||||
int
|
||||
eval_addtuple(typval_T *tv1, typval_T *tv2)
|
||||
{
|
||||
int vim9script = in_vim9script();
|
||||
typval_T var3;
|
||||
|
||||
if (vim9script && tv1->vval.v_tuple != NULL && tv2->vval.v_tuple != NULL
|
||||
&& tv1->vval.v_tuple->tv_type != NULL
|
||||
&& tv2->vval.v_tuple->tv_type != NULL)
|
||||
{
|
||||
if (!check_tuples_addable(tv1->vval.v_tuple->tv_type,
|
||||
tv2->vval.v_tuple->tv_type))
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
// concatenate tuples
|
||||
if (tuple_concat(tv1->vval.v_tuple, tv2->vval.v_tuple, &var3) == FAIL)
|
||||
{
|
||||
clear_tv(tv1);
|
||||
clear_tv(tv2);
|
||||
return FAIL;
|
||||
}
|
||||
clear_tv(tv1);
|
||||
*tv1 = var3;
|
||||
return OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* Left or right shift the number "tv1" by the number "tv2" and store the
|
||||
* result in "tv1".
|
||||
@@ -4231,6 +4409,7 @@ eval6(char_u **arg, typval_T *rettv, evalarg_T *evalarg)
|
||||
int concat;
|
||||
typval_T var2;
|
||||
int vim9script = in_vim9script();
|
||||
long op_lnum = SOURCING_LNUM;
|
||||
|
||||
// "." is only string concatenation when scriptversion is 1
|
||||
// "+=", "-=" and "..=" are assignments
|
||||
@@ -4259,7 +4438,8 @@ eval6(char_u **arg, typval_T *rettv, evalarg_T *evalarg)
|
||||
*arg = p;
|
||||
}
|
||||
if ((op != '+' || (rettv->v_type != VAR_LIST
|
||||
&& rettv->v_type != VAR_BLOB))
|
||||
&& rettv->v_type != VAR_TUPLE
|
||||
&& rettv->v_type != VAR_BLOB))
|
||||
&& (op == '.' || rettv->v_type != VAR_FLOAT)
|
||||
&& evaluate)
|
||||
{
|
||||
@@ -4302,6 +4482,8 @@ eval6(char_u **arg, typval_T *rettv, evalarg_T *evalarg)
|
||||
/*
|
||||
* Compute the result.
|
||||
*/
|
||||
// use the line of the operation for messages
|
||||
SOURCING_LNUM = op_lnum;
|
||||
if (op == '.')
|
||||
{
|
||||
if (eval_concat_str(rettv, &var2) == FAIL)
|
||||
@@ -4316,6 +4498,12 @@ eval6(char_u **arg, typval_T *rettv, evalarg_T *evalarg)
|
||||
if (eval_addlist(rettv, &var2) == FAIL)
|
||||
return FAIL;
|
||||
}
|
||||
else if (op == '+' && rettv->v_type == VAR_TUPLE
|
||||
&& var2.v_type == VAR_TUPLE)
|
||||
{
|
||||
if (eval_addtuple(rettv, &var2) == FAIL)
|
||||
return FAIL;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (eval_addsub_number(rettv, &var2, op) == FAIL)
|
||||
@@ -4681,13 +4869,23 @@ handle_predefined(char_u *s, int len, typval_T *rettv)
|
||||
return OK;
|
||||
}
|
||||
break;
|
||||
case 10: if (STRNCMP(s, "null_class", 10) == 0)
|
||||
case 10:
|
||||
if (STRNCMP(s, "null_", 5) != 0)
|
||||
break;
|
||||
// null_class
|
||||
if (STRNCMP(s + 5, "class", 5) == 0)
|
||||
{
|
||||
rettv->v_type = VAR_CLASS;
|
||||
rettv->vval.v_class = NULL;
|
||||
return OK;
|
||||
}
|
||||
break;
|
||||
if (STRNCMP(s + 5, "tuple", 5) == 0)
|
||||
{
|
||||
rettv->v_type = VAR_TUPLE;
|
||||
rettv->vval.v_tuple = NULL;
|
||||
return OK;
|
||||
}
|
||||
break;
|
||||
case 11: if (STRNCMP(s, "null_string", 11) == 0)
|
||||
{
|
||||
rettv->v_type = VAR_STRING;
|
||||
@@ -4796,16 +4994,26 @@ eval9_nested_expr(
|
||||
if (ret == NOTDONE)
|
||||
{
|
||||
*arg = skipwhite_and_linebreak(*arg + 1, evalarg);
|
||||
ret = eval1(arg, rettv, evalarg); // recursive!
|
||||
|
||||
*arg = skipwhite_and_linebreak(*arg, evalarg);
|
||||
if (**arg == ')')
|
||||
++*arg;
|
||||
else if (ret == OK)
|
||||
// empty tuple
|
||||
ret = eval_tuple(arg, rettv, evalarg, TRUE);
|
||||
else
|
||||
{
|
||||
emsg(_(e_missing_closing_paren));
|
||||
clear_tv(rettv);
|
||||
ret = FAIL;
|
||||
ret = eval1(arg, rettv, evalarg); // recursive!
|
||||
|
||||
*arg = skipwhite_and_linebreak(*arg, evalarg);
|
||||
|
||||
if (**arg == ',')
|
||||
// tuple
|
||||
ret = eval_tuple(arg, rettv, evalarg, TRUE);
|
||||
else if (**arg == ')')
|
||||
++*arg;
|
||||
else if (ret == OK)
|
||||
{
|
||||
emsg(_(e_missing_closing_paren));
|
||||
clear_tv(rettv);
|
||||
ret = FAIL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4896,6 +5104,7 @@ eval9_var_func_name(
|
||||
* $VAR environment variable
|
||||
* (expression) nested expression
|
||||
* [expr, expr] List
|
||||
* (expr, expr) Tuple
|
||||
* {arg, arg -> expr} Lambda
|
||||
* {key: val, key: val} Dictionary
|
||||
* #{key: val, key: val} Dictionary with literal keys
|
||||
@@ -4904,7 +5113,7 @@ eval9_var_func_name(
|
||||
* ! in front logical NOT
|
||||
* - in front unary minus
|
||||
* + in front unary plus (ignored)
|
||||
* trailing [] subscript in String or List
|
||||
* trailing [] subscript in String or List or Tuple
|
||||
* trailing .name entry in Dictionary
|
||||
* trailing ->name() method call
|
||||
*
|
||||
@@ -5049,6 +5258,7 @@ eval9(
|
||||
/*
|
||||
* nested expression: (expression).
|
||||
* or lambda: (arg) => expr
|
||||
* or tuple
|
||||
*/
|
||||
case '(': ret = eval9_nested_expr(arg, rettv, evalarg, evaluate);
|
||||
break;
|
||||
@@ -5484,7 +5694,8 @@ eval_index(
|
||||
var1.v_type = VAR_STRING;
|
||||
}
|
||||
|
||||
if (vim9script && rettv->v_type == VAR_LIST)
|
||||
if (vim9script && (rettv->v_type == VAR_LIST
|
||||
|| rettv->v_type == VAR_TUPLE))
|
||||
tv_get_number_chk(&var1, &error);
|
||||
else
|
||||
error = tv_get_string_chk(&var1) == NULL;
|
||||
@@ -5603,6 +5814,7 @@ check_can_index(typval_T *rettv, int evaluate, int verbose)
|
||||
|
||||
case VAR_STRING:
|
||||
case VAR_LIST:
|
||||
case VAR_TUPLE:
|
||||
case VAR_DICT:
|
||||
case VAR_BLOB:
|
||||
break;
|
||||
@@ -5735,6 +5947,16 @@ eval_index_inner(
|
||||
return FAIL;
|
||||
break;
|
||||
|
||||
case VAR_TUPLE:
|
||||
if (var1 == NULL)
|
||||
n1 = 0;
|
||||
if (var2 == NULL)
|
||||
n2 = VARNUM_MAX;
|
||||
if (tuple_slice_or_index(rettv->vval.v_tuple,
|
||||
is_range, n1, n2, exclusive, rettv, verbose) == FAIL)
|
||||
return FAIL;
|
||||
break;
|
||||
|
||||
case VAR_DICT:
|
||||
{
|
||||
dictitem_T *item;
|
||||
@@ -6079,6 +6301,51 @@ list_tv2string(
|
||||
return r;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return a textual representation of a Tuple in "tv".
|
||||
* If the memory is allocated "tofree" is set to it, otherwise NULL.
|
||||
* When "copyID" is not zero replace recursive lists with "...". When
|
||||
* "restore_copyID" is FALSE, repeated items in tuples are replaced with "...".
|
||||
* May return NULL.
|
||||
*/
|
||||
static char_u *
|
||||
tuple_tv2string(
|
||||
typval_T *tv,
|
||||
char_u **tofree,
|
||||
int copyID,
|
||||
int restore_copyID)
|
||||
{
|
||||
tuple_T *tuple = tv->vval.v_tuple;
|
||||
char_u *r = NULL;
|
||||
|
||||
if (tuple == NULL)
|
||||
{
|
||||
// NULL tuple is equivalent to an empty tuple.
|
||||
*tofree = NULL;
|
||||
r = (char_u *)"()";
|
||||
}
|
||||
else if (copyID != 0 && tuple->tv_copyID == copyID
|
||||
&& tuple->tv_items.ga_len > 0)
|
||||
{
|
||||
*tofree = NULL;
|
||||
r = (char_u *)"(...)";
|
||||
}
|
||||
else
|
||||
{
|
||||
int old_copyID;
|
||||
if (restore_copyID)
|
||||
old_copyID = tuple->tv_copyID;
|
||||
|
||||
tuple->tv_copyID = copyID;
|
||||
*tofree = tuple2string(tv, copyID, restore_copyID);
|
||||
if (restore_copyID)
|
||||
tuple->tv_copyID = old_copyID;
|
||||
r = *tofree;
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return a textual representation of a Dict in "tv".
|
||||
* If the memory is allocated "tofree" is set to it, otherwise NULL.
|
||||
@@ -6316,6 +6583,10 @@ echo_string_core(
|
||||
r = list_tv2string(tv, tofree, copyID, restore_copyID);
|
||||
break;
|
||||
|
||||
case VAR_TUPLE:
|
||||
r = tuple_tv2string(tv, tofree, copyID, restore_copyID);
|
||||
break;
|
||||
|
||||
case VAR_DICT:
|
||||
r = dict_tv2string(tv, tofree, copyID, restore_copyID);
|
||||
break;
|
||||
@@ -7257,6 +7528,23 @@ item_copy(
|
||||
if (to->vval.v_list == NULL)
|
||||
ret = FAIL;
|
||||
break;
|
||||
case VAR_TUPLE:
|
||||
to->v_type = VAR_TUPLE;
|
||||
to->v_lock = 0;
|
||||
if (from->vval.v_tuple == NULL)
|
||||
to->vval.v_tuple = NULL;
|
||||
else if (copyID != 0 && from->vval.v_tuple->tv_copyID == copyID)
|
||||
{
|
||||
// use the copy made earlier
|
||||
to->vval.v_tuple = from->vval.v_tuple->tv_copytuple;
|
||||
++to->vval.v_tuple->tv_refcount;
|
||||
}
|
||||
else
|
||||
to->vval.v_tuple = tuple_copy(from->vval.v_tuple,
|
||||
deep, top, copyID);
|
||||
if (to->vval.v_tuple == NULL)
|
||||
ret = FAIL;
|
||||
break;
|
||||
case VAR_BLOB:
|
||||
ret = blob_copy(from->vval.v_blob, to);
|
||||
break;
|
||||
|
||||
+407
-157
@@ -360,6 +360,15 @@ arg_list_string(type_T *type, type_T *decl_type UNUSED, argcontext_T *context)
|
||||
return check_arg_type(&t_list_string, type, context);
|
||||
}
|
||||
|
||||
/*
|
||||
* Check "type" is a tuple of 'any'.
|
||||
*/
|
||||
static int
|
||||
arg_tuple_any(type_T *type, type_T *decl_type UNUSED, argcontext_T *context)
|
||||
{
|
||||
return check_arg_type(&t_tuple_any, type, context);
|
||||
}
|
||||
|
||||
/*
|
||||
* Check "type" is a string.
|
||||
*/
|
||||
@@ -429,6 +438,42 @@ arg_list_or_blob_mod(
|
||||
return arg_type_modifiable(type, context->arg_idx + 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Check "type" is a list of 'any' or a tuple.
|
||||
*/
|
||||
static int
|
||||
arg_list_or_tuple(
|
||||
type_T *type,
|
||||
type_T *decl_type UNUSED,
|
||||
argcontext_T *context)
|
||||
{
|
||||
if (type->tt_type == VAR_LIST
|
||||
|| type->tt_type == VAR_TUPLE
|
||||
|| type_any_or_unknown(type))
|
||||
return OK;
|
||||
arg_type_mismatch(&t_list_any, type, context->arg_idx + 1);
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Check "type" is a list of 'any', a tuple or a blob.
|
||||
*/
|
||||
static int
|
||||
arg_list_or_tuple_or_blob(
|
||||
type_T *type,
|
||||
type_T *decl_type UNUSED,
|
||||
argcontext_T *context)
|
||||
{
|
||||
if (type->tt_type == VAR_LIST
|
||||
|| type->tt_type == VAR_TUPLE
|
||||
|| type->tt_type == VAR_BLOB
|
||||
|| type_any_or_unknown(type))
|
||||
return OK;
|
||||
arg_type_mismatch(&t_list_any, type, context->arg_idx + 1);
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check "type" is a string or a number
|
||||
*/
|
||||
@@ -461,7 +506,10 @@ arg_buffer(type_T *type, type_T *decl_type UNUSED, argcontext_T *context)
|
||||
* Check "type" is a buffer or a dict of any
|
||||
*/
|
||||
static int
|
||||
arg_buffer_or_dict_any(type_T *type, type_T *decl_type UNUSED, argcontext_T *context)
|
||||
arg_buffer_or_dict_any(
|
||||
type_T *type,
|
||||
type_T *decl_type UNUSED,
|
||||
argcontext_T *context)
|
||||
{
|
||||
if (type->tt_type == VAR_STRING
|
||||
|| type->tt_type == VAR_NUMBER
|
||||
@@ -490,7 +538,10 @@ arg_lnum(type_T *type, type_T *decl_type UNUSED, argcontext_T *context)
|
||||
* Check "type" is a string or a list of strings.
|
||||
*/
|
||||
static int
|
||||
arg_string_or_list_string(type_T *type, type_T *decl_type UNUSED, argcontext_T *context)
|
||||
arg_string_or_list_string(
|
||||
type_T *type,
|
||||
type_T *decl_type UNUSED,
|
||||
argcontext_T *context)
|
||||
{
|
||||
if (type->tt_type == VAR_STRING
|
||||
|| type_any_or_unknown(type))
|
||||
@@ -512,7 +563,10 @@ arg_string_or_list_string(type_T *type, type_T *decl_type UNUSED, argcontext_T *
|
||||
* Check "type" is a string or a list of 'any'
|
||||
*/
|
||||
static int
|
||||
arg_string_or_list_any(type_T *type, type_T *decl_type UNUSED, argcontext_T *context)
|
||||
arg_string_or_list_any(
|
||||
type_T *type,
|
||||
type_T *decl_type UNUSED,
|
||||
argcontext_T *context)
|
||||
{
|
||||
if (type->tt_type == VAR_STRING
|
||||
|| type->tt_type == VAR_LIST
|
||||
@@ -526,7 +580,10 @@ arg_string_or_list_any(type_T *type, type_T *decl_type UNUSED, argcontext_T *con
|
||||
* Check "type" is a string or a dict of 'any'
|
||||
*/
|
||||
static int
|
||||
arg_string_or_dict_any(type_T *type, type_T *decl_type UNUSED, argcontext_T *context)
|
||||
arg_string_or_dict_any(
|
||||
type_T *type,
|
||||
type_T *decl_type UNUSED,
|
||||
argcontext_T *context)
|
||||
{
|
||||
if (type->tt_type == VAR_STRING
|
||||
|| type->tt_type == VAR_DICT
|
||||
@@ -540,7 +597,10 @@ arg_string_or_dict_any(type_T *type, type_T *decl_type UNUSED, argcontext_T *con
|
||||
* Check "type" is a string or a blob
|
||||
*/
|
||||
static int
|
||||
arg_string_or_blob(type_T *type, type_T *decl_type UNUSED, argcontext_T *context)
|
||||
arg_string_or_blob(
|
||||
type_T *type,
|
||||
type_T *decl_type UNUSED,
|
||||
argcontext_T *context)
|
||||
{
|
||||
if (type->tt_type == VAR_STRING
|
||||
|| type->tt_type == VAR_BLOB
|
||||
@@ -579,7 +639,25 @@ arg_list_or_dict_mod(
|
||||
}
|
||||
|
||||
/*
|
||||
* Check "type" is a list of 'any' or a dict of 'any' or a blob.
|
||||
* Check "type" is a list of 'any', a tuple of 'any' or dict of 'any'.
|
||||
*/
|
||||
static int
|
||||
arg_list_or_tuple_or_dict(
|
||||
type_T *type,
|
||||
type_T *decl_type UNUSED,
|
||||
argcontext_T *context)
|
||||
{
|
||||
if (type->tt_type == VAR_LIST
|
||||
|| type->tt_type == VAR_TUPLE
|
||||
|| type->tt_type == VAR_DICT
|
||||
|| type_any_or_unknown(type))
|
||||
return OK;
|
||||
arg_type_mismatch(&t_list_any, type, context->arg_idx + 1);
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check "type" is a list of 'any', a dict of 'any' or a blob.
|
||||
* Also check if "type" is modifiable.
|
||||
*/
|
||||
static int
|
||||
@@ -601,7 +679,10 @@ arg_list_or_dict_or_blob_mod(
|
||||
* Check "type" is a list of 'any' or a dict of 'any' or a blob or a string.
|
||||
*/
|
||||
static int
|
||||
arg_list_or_dict_or_blob_or_string(type_T *type, type_T *decl_type UNUSED, argcontext_T *context)
|
||||
arg_list_or_dict_or_blob_or_string(
|
||||
type_T *type,
|
||||
type_T *decl_type UNUSED,
|
||||
argcontext_T *context)
|
||||
{
|
||||
if (type->tt_type == VAR_LIST
|
||||
|| type->tt_type == VAR_DICT
|
||||
@@ -628,12 +709,36 @@ arg_list_or_dict_or_blob_or_string_mod(
|
||||
return arg_type_modifiable(type, context->arg_idx + 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Check "type" is a list of 'any', a tuple of 'any', a dict of 'any', a blob
|
||||
* or a string.
|
||||
*/
|
||||
static int
|
||||
arg_list_tuple_dict_blob_or_string(
|
||||
type_T *type,
|
||||
type_T *decl_type UNUSED,
|
||||
argcontext_T *context)
|
||||
{
|
||||
if (type->tt_type == VAR_LIST
|
||||
|| type->tt_type == VAR_TUPLE
|
||||
|| type->tt_type == VAR_DICT
|
||||
|| type->tt_type == VAR_BLOB
|
||||
|| type->tt_type == VAR_STRING
|
||||
|| type_any_or_unknown(type))
|
||||
return OK;
|
||||
arg_type_mismatch(&t_list_any, type, context->arg_idx + 1);
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Check second argument of map(), filter(), foreach().
|
||||
*/
|
||||
static int
|
||||
check_map_filter_arg2(type_T *type, argcontext_T *context,
|
||||
filtermap_T filtermap)
|
||||
check_map_filter_arg2(
|
||||
type_T *type,
|
||||
argcontext_T *context,
|
||||
filtermap_T filtermap)
|
||||
{
|
||||
type_T *expected_member = NULL;
|
||||
type_T *(args[2]);
|
||||
@@ -801,7 +906,10 @@ arg_sort_how(type_T *type, type_T *decl_type UNUSED, argcontext_T *context)
|
||||
* Also accept a number, one and zero are accepted.
|
||||
*/
|
||||
static int
|
||||
arg_string_or_func(type_T *type, type_T *decl_type UNUSED, argcontext_T *context)
|
||||
arg_string_or_func(
|
||||
type_T *type,
|
||||
type_T *decl_type UNUSED,
|
||||
argcontext_T *context)
|
||||
{
|
||||
if (type->tt_type == VAR_STRING
|
||||
|| type->tt_type == VAR_PARTIAL
|
||||
@@ -835,12 +943,16 @@ varargs_class(type_T *type UNUSED,
|
||||
}
|
||||
|
||||
/*
|
||||
* Check "type" is a list of 'any' or a blob or a string.
|
||||
* Check "type" is a list of 'any', a tuple, a blob or a string.
|
||||
*/
|
||||
static int
|
||||
arg_string_list_or_blob(type_T *type, type_T *decl_type UNUSED, argcontext_T *context)
|
||||
arg_string_list_tuple_or_blob(
|
||||
type_T *type,
|
||||
type_T *decl_type UNUSED,
|
||||
argcontext_T *context)
|
||||
{
|
||||
if (type->tt_type == VAR_LIST
|
||||
|| type->tt_type == VAR_TUPLE
|
||||
|| type->tt_type == VAR_BLOB
|
||||
|| type->tt_type == VAR_STRING
|
||||
|| type_any_or_unknown(type))
|
||||
@@ -850,12 +962,12 @@ arg_string_list_or_blob(type_T *type, type_T *decl_type UNUSED, argcontext_T *co
|
||||
}
|
||||
|
||||
/*
|
||||
* Check "type" is a modifiable list of 'any' or a blob or a string.
|
||||
* Check "type" is a tuple or a modifiable list of 'any' or a blob or a string.
|
||||
*/
|
||||
static int
|
||||
arg_string_list_or_blob_mod(type_T *type, type_T *decl_type, argcontext_T *context)
|
||||
arg_reverse(type_T *type, type_T *decl_type, argcontext_T *context)
|
||||
{
|
||||
if (arg_string_list_or_blob(type, decl_type, context) == FAIL)
|
||||
if (arg_string_list_tuple_or_blob(type, decl_type, context) == FAIL)
|
||||
return FAIL;
|
||||
return arg_type_modifiable(type, context->arg_idx + 1);
|
||||
}
|
||||
@@ -901,7 +1013,10 @@ arg_same_as_prev(type_T *type, type_T *decl_type UNUSED, argcontext_T *context)
|
||||
* Must not be used for the first argcheck_T entry.
|
||||
*/
|
||||
static int
|
||||
arg_same_struct_as_prev(type_T *type, type_T *decl_type UNUSED, argcontext_T *context)
|
||||
arg_same_struct_as_prev(
|
||||
type_T *type,
|
||||
type_T *decl_type UNUSED,
|
||||
argcontext_T *context)
|
||||
{
|
||||
type_T *prev_type = context->arg_types[context->arg_idx - 1].type_curr;
|
||||
|
||||
@@ -935,7 +1050,10 @@ arg_item_of_prev(type_T *type, type_T *decl_type UNUSED, argcontext_T *context)
|
||||
* Check "type" is a string or a number or a list
|
||||
*/
|
||||
static int
|
||||
arg_str_or_nr_or_list(type_T *type, type_T *decl_type UNUSED, argcontext_T *context)
|
||||
arg_str_or_nr_or_list(
|
||||
type_T *type,
|
||||
type_T *decl_type UNUSED,
|
||||
argcontext_T *context)
|
||||
{
|
||||
if (type->tt_type == VAR_STRING
|
||||
|| type->tt_type == VAR_NUMBER
|
||||
@@ -950,7 +1068,10 @@ arg_str_or_nr_or_list(type_T *type, type_T *decl_type UNUSED, argcontext_T *cont
|
||||
* Check "type" is a dict of 'any' or a string
|
||||
*/
|
||||
static int
|
||||
arg_dict_any_or_string(type_T *type, type_T *decl_type UNUSED, argcontext_T *context)
|
||||
arg_dict_any_or_string(
|
||||
type_T *type,
|
||||
type_T *decl_type UNUSED,
|
||||
argcontext_T *context)
|
||||
{
|
||||
if (type->tt_type == VAR_DICT
|
||||
|| type->tt_type == VAR_STRING
|
||||
@@ -977,14 +1098,15 @@ arg_extend3(type_T *type, type_T *decl_type, argcontext_T *context)
|
||||
}
|
||||
|
||||
/*
|
||||
* Check "type" which is the first argument of get() (blob or list or dict or
|
||||
* funcref)
|
||||
* Check "type" which is the first argument of get() (a blob, a list, a tuple,
|
||||
* a dict or a funcref)
|
||||
*/
|
||||
static int
|
||||
arg_get1(type_T *type, type_T *decl_type UNUSED, argcontext_T *context)
|
||||
{
|
||||
if (type->tt_type == VAR_BLOB
|
||||
|| type->tt_type == VAR_LIST
|
||||
|| type->tt_type == VAR_TUPLE
|
||||
|| type->tt_type == VAR_DICT
|
||||
|| type->tt_type == VAR_FUNC
|
||||
|| type->tt_type == VAR_PARTIAL
|
||||
@@ -996,8 +1118,8 @@ arg_get1(type_T *type, type_T *decl_type UNUSED, argcontext_T *context)
|
||||
}
|
||||
|
||||
/*
|
||||
* Check "type" which is the first argument of len() (number or string or
|
||||
* blob or list or dict)
|
||||
* Check "type" which is the first argument of len() (a string, a number, a
|
||||
* blob, a list, a tuple, a dict or an object)
|
||||
*/
|
||||
static int
|
||||
arg_len1(type_T *type, type_T *decl_type UNUSED, argcontext_T *context)
|
||||
@@ -1006,6 +1128,7 @@ arg_len1(type_T *type, type_T *decl_type UNUSED, argcontext_T *context)
|
||||
|| type->tt_type == VAR_NUMBER
|
||||
|| type->tt_type == VAR_BLOB
|
||||
|| type->tt_type == VAR_LIST
|
||||
|| type->tt_type == VAR_TUPLE
|
||||
|| type->tt_type == VAR_DICT
|
||||
|| type->tt_type == VAR_OBJECT
|
||||
|| type_any_or_unknown(type))
|
||||
@@ -1032,8 +1155,8 @@ arg_remove2(type_T *type, type_T *decl_type, argcontext_T *context)
|
||||
}
|
||||
|
||||
/*
|
||||
* Check "type" which is the first argument of repeat() (string or number or
|
||||
* list or any)
|
||||
* Check "type" which is the first argument of repeat() (a string, a number, a
|
||||
* blob, a list, a tuple or any)
|
||||
*/
|
||||
static int
|
||||
arg_repeat1(type_T *type, type_T *decl_type UNUSED, argcontext_T *context)
|
||||
@@ -1042,6 +1165,7 @@ arg_repeat1(type_T *type, type_T *decl_type UNUSED, argcontext_T *context)
|
||||
|| type->tt_type == VAR_NUMBER
|
||||
|| type->tt_type == VAR_BLOB
|
||||
|| type->tt_type == VAR_LIST
|
||||
|| type->tt_type == VAR_TUPLE
|
||||
|| type_any_or_unknown(type))
|
||||
return OK;
|
||||
|
||||
@@ -1050,13 +1174,14 @@ arg_repeat1(type_T *type, type_T *decl_type UNUSED, argcontext_T *context)
|
||||
}
|
||||
|
||||
/*
|
||||
* Check "type" which is the first argument of slice() (list or blob or string
|
||||
* or any)
|
||||
* Check "type" which is the first argument of slice() (a list, a tuple, a
|
||||
* blob, a string or any)
|
||||
*/
|
||||
static int
|
||||
arg_slice1(type_T *type, type_T *decl_type UNUSED, argcontext_T *context)
|
||||
{
|
||||
if (type->tt_type == VAR_LIST
|
||||
|| type->tt_type == VAR_TUPLE
|
||||
|| type->tt_type == VAR_BLOB
|
||||
|| type->tt_type == VAR_STRING
|
||||
|| type_any_or_unknown(type))
|
||||
@@ -1067,19 +1192,23 @@ arg_slice1(type_T *type, type_T *decl_type UNUSED, argcontext_T *context)
|
||||
}
|
||||
|
||||
/*
|
||||
* Check "type" which is the first argument of count() (string or list or dict
|
||||
* or any)
|
||||
* Check "type" which is the first argument of count() (a string, a list, a
|
||||
* tuple, a dict or any)
|
||||
*/
|
||||
static int
|
||||
arg_string_or_list_or_dict(type_T *type, type_T *decl_type UNUSED, argcontext_T *context)
|
||||
arg_string_list_tuple_or_dict(
|
||||
type_T *type,
|
||||
type_T *decl_type UNUSED,
|
||||
argcontext_T *context)
|
||||
{
|
||||
if (type->tt_type == VAR_STRING
|
||||
|| type->tt_type == VAR_LIST
|
||||
|| type->tt_type == VAR_TUPLE
|
||||
|| type->tt_type == VAR_DICT
|
||||
|| type_any_or_unknown(type))
|
||||
return OK;
|
||||
|
||||
semsg(_(e_string_list_or_dict_required_for_argument_nr),
|
||||
semsg(_(e_string_list_tuple_or_dict_required_for_argument_nr),
|
||||
context->arg_idx + 1);
|
||||
return FAIL;
|
||||
}
|
||||
@@ -1114,11 +1243,12 @@ static argcheck_T arg1_dict_or_string[] = {arg_dict_any_or_string};
|
||||
static argcheck_T arg1_float_or_nr[] = {arg_float_or_nr};
|
||||
static argcheck_T arg1_job[] = {arg_job};
|
||||
static argcheck_T arg1_list_any[] = {arg_list_any};
|
||||
static argcheck_T arg1_tuple_any[] = {arg_tuple_any};
|
||||
static argcheck_T arg1_list_number[] = {arg_list_number};
|
||||
static argcheck_T arg1_string_or_list_or_blob_mod[] = {arg_string_list_or_blob_mod};
|
||||
static argcheck_T arg1_list_or_dict[] = {arg_list_or_dict};
|
||||
static argcheck_T arg1_reverse[] = {arg_reverse};
|
||||
static argcheck_T arg1_list_or_tuple_or_dict[] = {arg_list_or_tuple_or_dict};
|
||||
static argcheck_T arg1_list_string[] = {arg_list_string};
|
||||
static argcheck_T arg1_string_or_list_or_dict[] = {arg_string_or_list_or_dict};
|
||||
static argcheck_T arg1_string_list_tuple_or_dict[] = {arg_string_list_tuple_or_dict};
|
||||
static argcheck_T arg1_lnum[] = {arg_lnum};
|
||||
static argcheck_T arg1_number[] = {arg_number};
|
||||
static argcheck_T arg1_string[] = {arg_string};
|
||||
@@ -1141,7 +1271,6 @@ static argcheck_T arg2_float_or_nr[] = {arg_float_or_nr, arg_float_or_nr};
|
||||
static argcheck_T arg2_job_dict[] = {arg_job, arg_dict_any};
|
||||
static argcheck_T arg2_job_string_or_number[] = {arg_job, arg_string_or_nr};
|
||||
static argcheck_T arg2_list_any_number[] = {arg_list_any, arg_number};
|
||||
static argcheck_T arg2_list_any_string[] = {arg_list_any, arg_string};
|
||||
static argcheck_T arg2_list_number[] = {arg_list_number, arg_list_number};
|
||||
static argcheck_T arg2_list_number_bool[] = {arg_list_number, arg_bool};
|
||||
static argcheck_T arg2_list_string_dict[] = {arg_list_string, arg_dict_any};
|
||||
@@ -1168,6 +1297,7 @@ static argcheck_T arg2_string_or_list_dict[] = {arg_string_or_list_any, arg_dict
|
||||
static argcheck_T arg2_string_or_list_number[] = {arg_string_or_list_any, arg_number};
|
||||
static argcheck_T arg2_string_string_or_number[] = {arg_string, arg_string_or_nr};
|
||||
static argcheck_T arg2_blob_dict[] = {arg_blob, arg_dict_any};
|
||||
static argcheck_T arg2_list_or_tuple_string[] = {arg_list_or_tuple, arg_string};
|
||||
static argcheck_T arg3_any_list_dict[] = {arg_any, arg_list_any, arg_dict_any};
|
||||
static argcheck_T arg3_buffer_lnum_lnum[] = {arg_buffer, arg_lnum, arg_lnum};
|
||||
static argcheck_T arg3_buffer_number_number[] = {arg_buffer, arg_number, arg_number};
|
||||
@@ -1205,7 +1335,7 @@ static argcheck_T arg34_assert_inrange[] = {arg_float_or_nr, arg_float_or_nr, ar
|
||||
static argcheck_T arg4_browse[] = {arg_bool, arg_string, arg_string, arg_string};
|
||||
static argcheck_T arg23_chanexpr[] = {arg_chan_or_job, arg_any, arg_dict_any};
|
||||
static argcheck_T arg23_chanraw[] = {arg_chan_or_job, arg_string_or_blob, arg_dict_any};
|
||||
static argcheck_T arg24_count[] = {arg_string_or_list_or_dict, arg_any, arg_bool, arg_number};
|
||||
static argcheck_T arg24_count[] = {arg_string_list_tuple_or_dict, arg_any, arg_bool, arg_number};
|
||||
static argcheck_T arg13_cursor[] = {arg_cursor1, arg_number, arg_number};
|
||||
static argcheck_T arg12_deepcopy[] = {arg_any, arg_bool};
|
||||
static argcheck_T arg12_execute[] = {arg_string_or_list_string, arg_string};
|
||||
@@ -1215,14 +1345,14 @@ static argcheck_T arg23_extendnew[] = {arg_list_or_dict, arg_same_struct_as_prev
|
||||
static argcheck_T arg23_get[] = {arg_get1, arg_string_or_nr, arg_any};
|
||||
static argcheck_T arg14_glob[] = {arg_string, arg_bool, arg_bool, arg_bool};
|
||||
static argcheck_T arg25_globpath[] = {arg_string, arg_string, arg_bool, arg_bool, arg_bool};
|
||||
static argcheck_T arg24_index[] = {arg_list_or_blob, arg_item_of_prev, arg_number, arg_bool};
|
||||
static argcheck_T arg23_index[] = {arg_list_or_blob, arg_filter_func, arg_dict_any};
|
||||
static argcheck_T arg24_index[] = {arg_list_or_tuple_or_blob, arg_item_of_prev, arg_number, arg_bool};
|
||||
static argcheck_T arg23_index[] = {arg_list_or_tuple_or_blob, arg_filter_func, arg_dict_any};
|
||||
static argcheck_T arg23_insert[] = {arg_list_or_blob, arg_item_of_prev, arg_number};
|
||||
static argcheck_T arg1_len[] = {arg_len1};
|
||||
static argcheck_T arg3_libcall[] = {arg_string, arg_string, arg_string_or_nr};
|
||||
static argcheck_T arg14_maparg[] = {arg_string, arg_string, arg_bool, arg_bool};
|
||||
static argcheck_T arg2_filter[] = {arg_list_or_dict_or_blob_or_string_mod, arg_filter_func};
|
||||
static argcheck_T arg2_foreach[] = {arg_list_or_dict_or_blob_or_string, arg_foreach_func};
|
||||
static argcheck_T arg2_foreach[] = {arg_list_tuple_dict_blob_or_string, arg_foreach_func};
|
||||
static argcheck_T arg2_instanceof[] = {arg_object, varargs_class, NULL };
|
||||
static argcheck_T arg2_map[] = {arg_list_or_dict_or_blob_or_string_mod, arg_map_func};
|
||||
static argcheck_T arg2_mapnew[] = {arg_list_or_dict_or_blob_or_string, arg_any};
|
||||
@@ -1231,7 +1361,7 @@ static argcheck_T arg25_matchaddpos[] = {arg_string, arg_list_any, arg_number, a
|
||||
static argcheck_T arg23_matchstrlist[] = {arg_list_string, arg_string, arg_dict_any};
|
||||
static argcheck_T arg45_matchbufline[] = {arg_buffer, arg_string, arg_lnum, arg_lnum, arg_dict_any};
|
||||
static argcheck_T arg119_printf[] = {arg_string_or_nr, arg_any, arg_any, arg_any, arg_any, arg_any, arg_any, arg_any, arg_any, arg_any, arg_any, arg_any, arg_any, arg_any, arg_any, arg_any, arg_any, arg_any, arg_any};
|
||||
static argcheck_T arg23_reduce[] = {arg_string_list_or_blob, arg_any, arg_any};
|
||||
static argcheck_T arg23_reduce[] = {arg_string_list_tuple_or_blob, arg_any, arg_any};
|
||||
static argcheck_T arg24_remote_expr[] = {arg_string, arg_string, arg_string, arg_number};
|
||||
static argcheck_T arg23_remove[] = {arg_list_or_dict_or_blob_mod, arg_remove2, arg_number};
|
||||
static argcheck_T arg2_repeat[] = {arg_repeat1, arg_number};
|
||||
@@ -1364,6 +1494,13 @@ ret_list_regionpos(int argcount UNUSED,
|
||||
return &t_list_list_list_number;
|
||||
}
|
||||
static type_T *
|
||||
ret_tuple_any(int argcount UNUSED,
|
||||
type2_T *argtypes UNUSED,
|
||||
type_T **decl_type UNUSED)
|
||||
{
|
||||
return &t_tuple_any;
|
||||
}
|
||||
static type_T *
|
||||
ret_dict_any(int argcount UNUSED,
|
||||
type2_T *argtypes UNUSED,
|
||||
type_T **decl_type UNUSED)
|
||||
@@ -1457,6 +1594,7 @@ ret_slice(int argcount,
|
||||
case VAR_STRING: *decl_type = &t_string; break;
|
||||
case VAR_BLOB: *decl_type = &t_blob; break;
|
||||
case VAR_LIST: *decl_type = &t_list_any; break;
|
||||
case VAR_TUPLE: *decl_type = &t_tuple_any; break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
@@ -2288,7 +2426,7 @@ static funcentry_T global_functions[] =
|
||||
ret_number_bool, f_islocked},
|
||||
{"isnan", 1, 1, FEARG_1, arg1_float_or_nr,
|
||||
ret_number_bool, MATH_FUNC(f_isnan)},
|
||||
{"items", 1, 1, FEARG_1, arg1_string_or_list_or_dict,
|
||||
{"items", 1, 1, FEARG_1, arg1_string_list_tuple_or_dict,
|
||||
ret_list_items, f_items},
|
||||
{"job_getchannel", 1, 1, FEARG_1, arg1_job,
|
||||
ret_channel, JOB_FUNC(f_job_getchannel)},
|
||||
@@ -2302,7 +2440,7 @@ static funcentry_T global_functions[] =
|
||||
ret_string, JOB_FUNC(f_job_status)},
|
||||
{"job_stop", 1, 2, FEARG_1, arg2_job_string_or_number,
|
||||
ret_number_bool, JOB_FUNC(f_job_stop)},
|
||||
{"join", 1, 2, FEARG_1, arg2_list_any_string,
|
||||
{"join", 1, 2, FEARG_1, arg2_list_or_tuple_string,
|
||||
ret_string, f_join},
|
||||
{"js_decode", 1, 1, FEARG_1, arg1_string,
|
||||
ret_any, f_js_decode},
|
||||
@@ -2334,6 +2472,8 @@ static funcentry_T global_functions[] =
|
||||
ret_blob, f_list2blob},
|
||||
{"list2str", 1, 2, FEARG_1, arg2_list_number_bool,
|
||||
ret_string, f_list2str},
|
||||
{"list2tuple", 1, 1, FEARG_1, arg1_list_any,
|
||||
ret_tuple_any, f_list2tuple},
|
||||
{"listener_add", 1, 2, FEARG_2, arg2_any_buffer,
|
||||
ret_number, f_listener_add},
|
||||
{"listener_flush", 0, 1, FEARG_1, arg1_buffer,
|
||||
@@ -2392,7 +2532,7 @@ static funcentry_T global_functions[] =
|
||||
ret_list_any, f_matchstrlist},
|
||||
{"matchstrpos", 2, 4, FEARG_1, arg24_match_func,
|
||||
ret_list_any, f_matchstrpos},
|
||||
{"max", 1, 1, FEARG_1, arg1_list_or_dict,
|
||||
{"max", 1, 1, FEARG_1, arg1_list_or_tuple_or_dict,
|
||||
ret_number, f_max},
|
||||
{"menu_info", 1, 2, FEARG_1, arg2_string,
|
||||
ret_dict_any,
|
||||
@@ -2402,7 +2542,7 @@ static funcentry_T global_functions[] =
|
||||
NULL
|
||||
#endif
|
||||
},
|
||||
{"min", 1, 1, FEARG_1, arg1_list_or_dict,
|
||||
{"min", 1, 1, FEARG_1, arg1_list_or_tuple_or_dict,
|
||||
ret_number, f_min},
|
||||
{"mkdir", 1, 3, FEARG_1, arg3_string_string_number,
|
||||
ret_number_bool, f_mkdir},
|
||||
@@ -2588,7 +2728,7 @@ static funcentry_T global_functions[] =
|
||||
ret_repeat, f_repeat},
|
||||
{"resolve", 1, 1, FEARG_1, arg1_string,
|
||||
ret_string, f_resolve},
|
||||
{"reverse", 1, 1, FEARG_1, arg1_string_or_list_or_blob_mod,
|
||||
{"reverse", 1, 1, FEARG_1, arg1_reverse,
|
||||
ret_first_arg, f_reverse},
|
||||
{"round", 1, 1, FEARG_1, arg1_float_or_nr,
|
||||
ret_float, f_round},
|
||||
@@ -2918,6 +3058,8 @@ static funcentry_T global_functions[] =
|
||||
ret_func_any, f_test_null_partial},
|
||||
{"test_null_string", 0, 0, 0, NULL,
|
||||
ret_string, f_test_null_string},
|
||||
{"test_null_tuple", 0, 0, 0, NULL,
|
||||
ret_tuple_any, f_test_null_tuple},
|
||||
{"test_option_not_set", 1, 1, FEARG_1, arg1_string,
|
||||
ret_void, f_test_option_not_set},
|
||||
{"test_override", 2, 2, FEARG_2, arg2_string_number,
|
||||
@@ -2954,6 +3096,8 @@ static funcentry_T global_functions[] =
|
||||
ret_string, f_trim},
|
||||
{"trunc", 1, 1, FEARG_1, arg1_float_or_nr,
|
||||
ret_float, f_trunc},
|
||||
{"tuple2list", 1, 1, FEARG_1, arg1_tuple_any,
|
||||
ret_list_any, f_tuple2list},
|
||||
{"type", 1, 1, FEARG_1|FE_X, NULL,
|
||||
ret_number, f_type},
|
||||
{"typename", 1, 1, FEARG_1|FE_X, NULL,
|
||||
@@ -4226,6 +4370,9 @@ f_empty(typval_T *argvars, typval_T *rettv)
|
||||
n = argvars[0].vval.v_list == NULL
|
||||
|| argvars[0].vval.v_list->lv_len == 0;
|
||||
break;
|
||||
case VAR_TUPLE:
|
||||
n = tuple_len(argvars[0].vval.v_tuple) == 0;
|
||||
break;
|
||||
case VAR_DICT:
|
||||
n = argvars[0].vval.v_dict == NULL
|
||||
|| argvars[0].vval.v_dict->dv_hashtab.ht_used == 0;
|
||||
@@ -5263,6 +5410,7 @@ f_get(typval_T *argvars, typval_T *rettv)
|
||||
{
|
||||
listitem_T *li;
|
||||
list_T *l;
|
||||
tuple_T *tuple;
|
||||
dictitem_T *di;
|
||||
dict_T *d;
|
||||
typval_T *tv = NULL;
|
||||
@@ -5298,6 +5446,18 @@ f_get(typval_T *argvars, typval_T *rettv)
|
||||
tv = &li->li_tv;
|
||||
}
|
||||
}
|
||||
else if (argvars[0].v_type == VAR_TUPLE)
|
||||
{
|
||||
if ((tuple = argvars[0].vval.v_tuple) != NULL)
|
||||
{
|
||||
int error = FALSE;
|
||||
long idx;
|
||||
|
||||
idx = (long)tv_get_number_chk(&argvars[1], &error);
|
||||
if (!error)
|
||||
tv = tuple_find(tuple, idx);
|
||||
}
|
||||
}
|
||||
else if (argvars[0].v_type == VAR_DICT)
|
||||
{
|
||||
if ((d = argvars[0].vval.v_dict) != NULL)
|
||||
@@ -5400,7 +5560,7 @@ f_get(typval_T *argvars, typval_T *rettv)
|
||||
}
|
||||
}
|
||||
else
|
||||
semsg(_(e_argument_of_str_must_be_list_dictionary_or_blob), "get()");
|
||||
semsg(_(e_argument_of_str_must_be_list_tuple_dictionary_or_blob), "get()");
|
||||
|
||||
if (tv == NULL)
|
||||
{
|
||||
@@ -7811,6 +7971,7 @@ f_id(typval_T *argvars, typval_T *rettv)
|
||||
switch (argvars[0].v_type)
|
||||
{
|
||||
case VAR_LIST:
|
||||
case VAR_TUPLE:
|
||||
case VAR_DICT:
|
||||
case VAR_OBJECT:
|
||||
case VAR_JOB:
|
||||
@@ -7837,67 +7998,84 @@ f_id(typval_T *argvars, typval_T *rettv)
|
||||
}
|
||||
|
||||
/*
|
||||
* "index()" function
|
||||
* index() function for a blob
|
||||
*/
|
||||
static void
|
||||
f_index(typval_T *argvars, typval_T *rettv)
|
||||
index_func_blob(typval_T *argvars, typval_T *rettv)
|
||||
{
|
||||
list_T *l;
|
||||
listitem_T *item;
|
||||
typval_T tv;
|
||||
blob_T *b;
|
||||
long idx = 0;
|
||||
int start = 0;
|
||||
int error = FALSE;
|
||||
int ic = FALSE;
|
||||
|
||||
b = argvars[0].vval.v_blob;
|
||||
if (b == NULL)
|
||||
return;
|
||||
|
||||
if (argvars[2].v_type != VAR_UNKNOWN)
|
||||
{
|
||||
start = tv_get_number_chk(&argvars[2], &error);
|
||||
if (error)
|
||||
return;
|
||||
}
|
||||
|
||||
if (start < 0)
|
||||
{
|
||||
start = blob_len(b) + start;
|
||||
if (start < 0)
|
||||
start = 0;
|
||||
}
|
||||
|
||||
for (int idx = start; idx < blob_len(b); ++idx)
|
||||
{
|
||||
tv.v_type = VAR_NUMBER;
|
||||
tv.vval.v_number = blob_get(b, idx);
|
||||
if (tv_equal(&tv, &argvars[1], ic))
|
||||
{
|
||||
rettv->vval.v_number = idx;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* index() function for a tuple
|
||||
*/
|
||||
static void
|
||||
index_func_tuple(typval_T *argvars, typval_T *rettv)
|
||||
{
|
||||
tuple_T *tuple = argvars[0].vval.v_tuple;
|
||||
int ic = FALSE;
|
||||
int error = FALSE;
|
||||
|
||||
rettv->vval.v_number = -1;
|
||||
|
||||
if (in_vim9script()
|
||||
&& (check_for_list_or_blob_arg(argvars, 0) == FAIL
|
||||
|| (argvars[0].v_type == VAR_BLOB
|
||||
&& check_for_number_arg(argvars, 1) == FAIL)
|
||||
|| check_for_opt_number_arg(argvars, 2) == FAIL
|
||||
|| (argvars[2].v_type != VAR_UNKNOWN
|
||||
&& check_for_opt_bool_arg(argvars, 3) == FAIL)))
|
||||
if (tuple == NULL)
|
||||
return;
|
||||
|
||||
if (argvars[0].v_type == VAR_BLOB)
|
||||
int start_idx = 0;
|
||||
if (argvars[2].v_type != VAR_UNKNOWN)
|
||||
{
|
||||
typval_T tv;
|
||||
int start = 0;
|
||||
|
||||
if (argvars[2].v_type != VAR_UNKNOWN)
|
||||
{
|
||||
start = tv_get_number_chk(&argvars[2], &error);
|
||||
if (error)
|
||||
return;
|
||||
}
|
||||
b = argvars[0].vval.v_blob;
|
||||
if (b == NULL)
|
||||
start_idx = tv_get_number_chk(&argvars[2], &error);
|
||||
if (!error && argvars[3].v_type != VAR_UNKNOWN)
|
||||
ic = (int)tv_get_bool_chk(&argvars[3], &error);
|
||||
if (error)
|
||||
return;
|
||||
if (start < 0)
|
||||
{
|
||||
start = blob_len(b) + start;
|
||||
if (start < 0)
|
||||
start = 0;
|
||||
}
|
||||
}
|
||||
|
||||
for (idx = start; idx < blob_len(b); ++idx)
|
||||
{
|
||||
tv.v_type = VAR_NUMBER;
|
||||
tv.vval.v_number = blob_get(b, idx);
|
||||
if (tv_equal(&tv, &argvars[1], ic))
|
||||
{
|
||||
rettv->vval.v_number = idx;
|
||||
return;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
else if (argvars[0].v_type != VAR_LIST)
|
||||
{
|
||||
emsg(_(e_list_or_blob_required));
|
||||
return;
|
||||
}
|
||||
rettv->vval.v_number = index_tuple(tuple, &argvars[1], start_idx, ic);
|
||||
}
|
||||
|
||||
/*
|
||||
* index() function for a list
|
||||
*/
|
||||
static void
|
||||
index_func_list(typval_T *argvars, typval_T *rettv)
|
||||
{
|
||||
list_T *l;
|
||||
listitem_T *item;
|
||||
long idx = 0;
|
||||
int ic = FALSE;
|
||||
int error = FALSE;
|
||||
|
||||
l = argvars[0].vval.v_list;
|
||||
if (l == NULL)
|
||||
@@ -7925,12 +8103,39 @@ f_index(typval_T *argvars, typval_T *rettv)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* "index()" function
|
||||
*/
|
||||
static void
|
||||
f_index(typval_T *argvars, typval_T *rettv)
|
||||
{
|
||||
rettv->vval.v_number = -1;
|
||||
|
||||
if (in_vim9script()
|
||||
&& (check_for_list_or_tuple_or_blob_arg(argvars, 0) == FAIL
|
||||
|| (argvars[0].v_type == VAR_BLOB
|
||||
&& check_for_number_arg(argvars, 1) == FAIL)
|
||||
|| check_for_opt_number_arg(argvars, 2) == FAIL
|
||||
|| (argvars[2].v_type != VAR_UNKNOWN
|
||||
&& check_for_opt_bool_arg(argvars, 3) == FAIL)))
|
||||
return;
|
||||
|
||||
if (argvars[0].v_type == VAR_BLOB)
|
||||
index_func_blob(argvars, rettv);
|
||||
else if (argvars[0].v_type == VAR_TUPLE)
|
||||
index_func_tuple(argvars, rettv);
|
||||
else if (argvars[0].v_type == VAR_LIST)
|
||||
index_func_list(argvars, rettv);
|
||||
else
|
||||
emsg(_(e_list_or_blob_required));
|
||||
}
|
||||
|
||||
/*
|
||||
* Evaluate 'expr' with the v:key and v:val arguments and return the result.
|
||||
* The expression is expected to return a boolean value. The caller should set
|
||||
* the VV_KEY and VV_VAL vim variables before calling this function.
|
||||
*/
|
||||
static int
|
||||
int
|
||||
indexof_eval_expr(typval_T *expr)
|
||||
{
|
||||
typval_T argv[3];
|
||||
@@ -8053,7 +8258,7 @@ f_indexof(typval_T *argvars, typval_T *rettv)
|
||||
|
||||
rettv->vval.v_number = -1;
|
||||
|
||||
if (check_for_list_or_blob_arg(argvars, 0) == FAIL
|
||||
if (check_for_list_or_tuple_or_blob_arg(argvars, 0) == FAIL
|
||||
|| check_for_string_or_func_arg(argvars, 1) == FAIL
|
||||
|| check_for_opt_dict_arg(argvars, 2) == FAIL)
|
||||
return;
|
||||
@@ -8079,6 +8284,9 @@ f_indexof(typval_T *argvars, typval_T *rettv)
|
||||
if (argvars[0].v_type == VAR_BLOB)
|
||||
rettv->vval.v_number = indexof_blob(argvars[0].vval.v_blob, startidx,
|
||||
&argvars[1]);
|
||||
else if (argvars[0].v_type == VAR_TUPLE)
|
||||
rettv->vval.v_number = indexof_tuple(argvars[0].vval.v_tuple, startidx,
|
||||
&argvars[1]);
|
||||
else
|
||||
rettv->vval.v_number = indexof_list(argvars[0].vval.v_list, startidx,
|
||||
&argvars[1]);
|
||||
@@ -8488,6 +8696,9 @@ f_len(typval_T *argvars, typval_T *rettv)
|
||||
case VAR_LIST:
|
||||
rettv->vval.v_number = list_len(argvars[0].vval.v_list);
|
||||
break;
|
||||
case VAR_TUPLE:
|
||||
rettv->vval.v_number = tuple_len(argvars[0].vval.v_tuple);
|
||||
break;
|
||||
case VAR_DICT:
|
||||
rettv->vval.v_number = dict_len(argvars[0].vval.v_dict);
|
||||
break;
|
||||
@@ -9229,7 +9440,8 @@ max_min(typval_T *argvars, typval_T *rettv, int domax)
|
||||
varnumber_T i;
|
||||
int error = FALSE;
|
||||
|
||||
if (in_vim9script() && check_for_list_or_dict_arg(argvars, 0) == FAIL)
|
||||
if (in_vim9script() &&
|
||||
check_for_list_or_tuple_or_dict_arg(argvars, 0) == FAIL)
|
||||
return;
|
||||
|
||||
if (argvars[0].v_type == VAR_LIST)
|
||||
@@ -9271,6 +9483,12 @@ max_min(typval_T *argvars, typval_T *rettv, int domax)
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (argvars[0].v_type == VAR_TUPLE)
|
||||
{
|
||||
n = tuple_max_min(argvars[0].vval.v_tuple, domax, &error);
|
||||
if (error)
|
||||
return;
|
||||
}
|
||||
else if (argvars[0].v_type == VAR_DICT)
|
||||
{
|
||||
dict_T *d;
|
||||
@@ -10076,83 +10294,114 @@ f_rename(typval_T *argvars, typval_T *rettv)
|
||||
}
|
||||
|
||||
/*
|
||||
* "repeat()" function
|
||||
* Repeat the list "l" "n" times and set "rettv" to the new list.
|
||||
*/
|
||||
static void
|
||||
f_repeat(typval_T *argvars, typval_T *rettv)
|
||||
repeat_list(list_T *l, int n, typval_T *rettv)
|
||||
{
|
||||
if (rettv_list_alloc(rettv) == FAIL
|
||||
|| l == NULL
|
||||
|| n <= 0)
|
||||
return;
|
||||
|
||||
while (n-- > 0)
|
||||
if (list_extend(rettv->vval.v_list, l, NULL) == FAIL)
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Repeat the blob "b" "n" times and set "rettv" to the new blob.
|
||||
*/
|
||||
static void
|
||||
repeat_blob(typval_T *blob_tv, int n, typval_T *rettv)
|
||||
{
|
||||
int slen;
|
||||
int len;
|
||||
int i;
|
||||
blob_T *blob = blob_tv->vval.v_blob;
|
||||
|
||||
if (rettv_blob_alloc(rettv) == FAIL
|
||||
|| blob == NULL
|
||||
|| n <= 0)
|
||||
return;
|
||||
|
||||
slen = blob->bv_ga.ga_len;
|
||||
len = (int)slen * n;
|
||||
if (len <= 0)
|
||||
return;
|
||||
|
||||
if (ga_grow(&rettv->vval.v_blob->bv_ga, len) == FAIL)
|
||||
return;
|
||||
|
||||
rettv->vval.v_blob->bv_ga.ga_len = len;
|
||||
|
||||
for (i = 0; i < slen; ++i)
|
||||
if (blob_get(blob, i) != 0)
|
||||
break;
|
||||
|
||||
if (i == slen)
|
||||
// No need to copy since all bytes are already zero
|
||||
return;
|
||||
|
||||
for (i = 0; i < n; ++i)
|
||||
blob_set_range(rettv->vval.v_blob,
|
||||
(long)i * slen, ((long)i + 1) * slen - 1, blob_tv);
|
||||
}
|
||||
|
||||
/*
|
||||
* Repeat the string "str" "n" times and set "rettv" to the new string.
|
||||
*/
|
||||
static void
|
||||
repeat_string(typval_T *str_tv, int n, typval_T *rettv)
|
||||
{
|
||||
char_u *p;
|
||||
varnumber_T n;
|
||||
int slen;
|
||||
int len;
|
||||
char_u *r;
|
||||
int i;
|
||||
|
||||
p = tv_get_string(str_tv);
|
||||
rettv->v_type = VAR_STRING;
|
||||
rettv->vval.v_string = NULL;
|
||||
|
||||
slen = (int)STRLEN(p);
|
||||
len = slen * n;
|
||||
if (len <= 0)
|
||||
return;
|
||||
|
||||
r = alloc(len + 1);
|
||||
if (r == NULL)
|
||||
return;
|
||||
|
||||
for (i = 0; i < n; i++)
|
||||
mch_memmove(r + i * slen, p, (size_t)slen);
|
||||
r[len] = NUL;
|
||||
|
||||
rettv->vval.v_string = r;
|
||||
}
|
||||
|
||||
/*
|
||||
* "repeat()" function
|
||||
*/
|
||||
static void
|
||||
f_repeat(typval_T *argvars, typval_T *rettv)
|
||||
{
|
||||
varnumber_T n;
|
||||
|
||||
if (in_vim9script()
|
||||
&& (check_for_string_or_number_or_list_or_blob_arg(argvars, 0)
|
||||
== FAIL
|
||||
&& (check_for_repeat_func_arg(argvars, 0) == FAIL
|
||||
|| check_for_number_arg(argvars, 1) == FAIL))
|
||||
return;
|
||||
|
||||
n = tv_get_number(&argvars[1]);
|
||||
if (argvars[0].v_type == VAR_LIST)
|
||||
{
|
||||
if (rettv_list_alloc(rettv) == OK && argvars[0].vval.v_list != NULL)
|
||||
while (n-- > 0)
|
||||
if (list_extend(rettv->vval.v_list,
|
||||
argvars[0].vval.v_list, NULL) == FAIL)
|
||||
break;
|
||||
}
|
||||
repeat_list(argvars[0].vval.v_list, n, rettv);
|
||||
else if (argvars[0].v_type == VAR_TUPLE)
|
||||
tuple_repeat(argvars[0].vval.v_tuple, n, rettv);
|
||||
else if (argvars[0].v_type == VAR_BLOB)
|
||||
{
|
||||
if (rettv_blob_alloc(rettv) == FAIL
|
||||
|| argvars[0].vval.v_blob == NULL
|
||||
|| n <= 0)
|
||||
return;
|
||||
|
||||
slen = argvars[0].vval.v_blob->bv_ga.ga_len;
|
||||
len = (int)slen * n;
|
||||
if (len <= 0)
|
||||
return;
|
||||
|
||||
if (ga_grow(&rettv->vval.v_blob->bv_ga, len) == FAIL)
|
||||
return;
|
||||
|
||||
rettv->vval.v_blob->bv_ga.ga_len = len;
|
||||
|
||||
for (i = 0; i < slen; ++i)
|
||||
if (blob_get(argvars[0].vval.v_blob, i) != 0)
|
||||
break;
|
||||
|
||||
if (i == slen)
|
||||
// No need to copy since all bytes are already zero
|
||||
return;
|
||||
|
||||
for (i = 0; i < n; ++i)
|
||||
blob_set_range(rettv->vval.v_blob,
|
||||
(long)i * slen, ((long)i + 1) * slen - 1, argvars);
|
||||
}
|
||||
repeat_blob(&argvars[0], n, rettv);
|
||||
else
|
||||
{
|
||||
p = tv_get_string(&argvars[0]);
|
||||
rettv->v_type = VAR_STRING;
|
||||
rettv->vval.v_string = NULL;
|
||||
|
||||
slen = (int)STRLEN(p);
|
||||
len = slen * n;
|
||||
if (len <= 0)
|
||||
return;
|
||||
|
||||
r = alloc(len + 1);
|
||||
if (r != NULL)
|
||||
{
|
||||
for (i = 0; i < n; i++)
|
||||
mch_memmove(r + i * slen, p, (size_t)slen);
|
||||
r[len] = NUL;
|
||||
}
|
||||
|
||||
rettv->vval.v_string = r;
|
||||
}
|
||||
repeat_string(&argvars[0], n, rettv);
|
||||
}
|
||||
|
||||
#define SP_NOMOVE 0x01 // don't move cursor
|
||||
@@ -12191,6 +12440,7 @@ f_type(typval_T *argvars, typval_T *rettv)
|
||||
case VAR_PARTIAL:
|
||||
case VAR_FUNC: n = VAR_TYPE_FUNC; break;
|
||||
case VAR_LIST: n = VAR_TYPE_LIST; break;
|
||||
case VAR_TUPLE: n = VAR_TYPE_TUPLE; break;
|
||||
case VAR_DICT: n = VAR_TYPE_DICT; break;
|
||||
case VAR_FLOAT: n = VAR_TYPE_FLOAT; break;
|
||||
case VAR_BOOL: n = VAR_TYPE_BOOL; break;
|
||||
|
||||
+117
-33
@@ -162,6 +162,7 @@ static struct vimvar
|
||||
{VV_NAME("t_enum", VAR_NUMBER), NULL, VV_RO},
|
||||
{VV_NAME("t_enumvalue", VAR_NUMBER), NULL, VV_RO},
|
||||
{VV_NAME("stacktrace", VAR_LIST), &t_list_dict_any, VV_RO},
|
||||
{VV_NAME("t_tuple", VAR_NUMBER), NULL, VV_RO},
|
||||
};
|
||||
|
||||
// shorthand
|
||||
@@ -265,8 +266,9 @@ evalvars_init(void)
|
||||
set_vim_var_nr(VV_TYPE_CLASS, VAR_TYPE_CLASS);
|
||||
set_vim_var_nr(VV_TYPE_OBJECT, VAR_TYPE_OBJECT);
|
||||
set_vim_var_nr(VV_TYPE_TYPEALIAS, VAR_TYPE_TYPEALIAS);
|
||||
set_vim_var_nr(VV_TYPE_ENUM, VAR_TYPE_ENUM);
|
||||
set_vim_var_nr(VV_TYPE_ENUM, VAR_TYPE_ENUM);
|
||||
set_vim_var_nr(VV_TYPE_ENUMVALUE, VAR_TYPE_ENUMVALUE);
|
||||
set_vim_var_nr(VV_TYPE_TUPLE, VAR_TYPE_TUPLE);
|
||||
|
||||
set_vim_var_nr(VV_ECHOSPACE, sc_col - 1);
|
||||
|
||||
@@ -321,13 +323,13 @@ evalvars_clear(void)
|
||||
int
|
||||
garbage_collect_globvars(int copyID)
|
||||
{
|
||||
return set_ref_in_ht(&globvarht, copyID, NULL);
|
||||
return set_ref_in_ht(&globvarht, copyID, NULL, NULL);
|
||||
}
|
||||
|
||||
int
|
||||
garbage_collect_vimvars(int copyID)
|
||||
{
|
||||
return set_ref_in_ht(&vimvarht, copyID, NULL);
|
||||
return set_ref_in_ht(&vimvarht, copyID, NULL, NULL);
|
||||
}
|
||||
|
||||
int
|
||||
@@ -340,7 +342,7 @@ garbage_collect_scriptvars(int copyID)
|
||||
|
||||
for (i = 1; i <= script_items.ga_len; ++i)
|
||||
{
|
||||
abort = abort || set_ref_in_ht(&SCRIPT_VARS(i), copyID, NULL);
|
||||
abort = abort || set_ref_in_ht(&SCRIPT_VARS(i), copyID, NULL, NULL);
|
||||
|
||||
si = SCRIPT_ITEM(i);
|
||||
for (idx = 0; idx < si->sn_var_vals.ga_len; ++idx)
|
||||
@@ -348,7 +350,7 @@ garbage_collect_scriptvars(int copyID)
|
||||
svar_T *sv = ((svar_T *)si->sn_var_vals.ga_data) + idx;
|
||||
|
||||
if (sv->sv_name != NULL)
|
||||
abort = abort || set_ref_in_item(sv->sv_tv, copyID, NULL, NULL);
|
||||
abort = abort || set_ref_in_item(sv->sv_tv, copyID, NULL, NULL, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1234,10 +1236,13 @@ ex_let_vars(
|
||||
{
|
||||
char_u *arg = arg_start;
|
||||
list_T *l;
|
||||
tuple_T *tuple = NULL;
|
||||
int i;
|
||||
int var_idx = 0;
|
||||
listitem_T *item;
|
||||
listitem_T *item = NULL;
|
||||
typval_T ltv;
|
||||
int is_list = tv->v_type == VAR_LIST;
|
||||
int idx;
|
||||
|
||||
if (tv->v_type == VAR_VOID)
|
||||
{
|
||||
@@ -1253,58 +1258,121 @@ ex_let_vars(
|
||||
}
|
||||
|
||||
// ":let [v1, v2] = list" or ":for [v1, v2] in listlist"
|
||||
if (tv->v_type != VAR_LIST || (l = tv->vval.v_list) == NULL)
|
||||
// or
|
||||
// ":let [v1, v2] = tuple" or ":for [v1, v2] in tupletuple"
|
||||
if (tv->v_type != VAR_LIST && tv->v_type != VAR_TUPLE)
|
||||
{
|
||||
emsg(_(e_list_required));
|
||||
emsg(_(e_list_or_tuple_required));
|
||||
return FAIL;
|
||||
}
|
||||
if (is_list)
|
||||
{
|
||||
l = tv->vval.v_list;
|
||||
if (l == NULL)
|
||||
{
|
||||
emsg(_(e_list_required));
|
||||
return FAIL;
|
||||
}
|
||||
i = list_len(l);
|
||||
}
|
||||
else
|
||||
{
|
||||
tuple = tv->vval.v_tuple;
|
||||
if (tuple == NULL)
|
||||
{
|
||||
emsg(_(e_tuple_required));
|
||||
return FAIL;
|
||||
}
|
||||
i = tuple_len(tuple);
|
||||
}
|
||||
|
||||
i = list_len(l);
|
||||
if (semicolon == 0 && var_count < i)
|
||||
{
|
||||
emsg(_(e_less_targets_than_list_items));
|
||||
emsg(_(is_list ? e_less_targets_than_list_items
|
||||
: e_less_targets_than_tuple_items));
|
||||
return FAIL;
|
||||
}
|
||||
if (var_count - semicolon > i)
|
||||
{
|
||||
emsg(_(e_more_targets_than_list_items));
|
||||
emsg(_(is_list ? e_more_targets_than_list_items
|
||||
: e_more_targets_than_tuple_items));
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
CHECK_LIST_MATERIALIZE(l);
|
||||
item = l->lv_first;
|
||||
if (is_list)
|
||||
{
|
||||
CHECK_LIST_MATERIALIZE(l);
|
||||
item = l->lv_first;
|
||||
}
|
||||
else
|
||||
idx = 0;
|
||||
|
||||
while (*arg != ']')
|
||||
{
|
||||
arg = skipwhite(arg + 1);
|
||||
++var_idx;
|
||||
arg = ex_let_one(arg, &item->li_tv, TRUE,
|
||||
flags | ASSIGN_UNPACK, (char_u *)",;]", op, var_idx);
|
||||
item = item->li_next;
|
||||
arg = ex_let_one(arg, is_list ? &item->li_tv : TUPLE_ITEM(tuple, idx),
|
||||
TRUE, flags | ASSIGN_UNPACK, (char_u *)",;]", op,
|
||||
var_idx);
|
||||
if (is_list)
|
||||
item = item->li_next;
|
||||
else
|
||||
idx++;
|
||||
if (arg == NULL)
|
||||
return FAIL;
|
||||
|
||||
arg = skipwhite(arg);
|
||||
if (*arg == ';')
|
||||
{
|
||||
// Put the rest of the list (may be empty) in the var after ';'.
|
||||
// Create a new list for this.
|
||||
l = list_alloc();
|
||||
if (l == NULL)
|
||||
return FAIL;
|
||||
while (item != NULL)
|
||||
// Put the rest of the list or tuple (may be empty) in the var
|
||||
// after ';'. Create a new list or tuple for this.
|
||||
if (is_list)
|
||||
{
|
||||
list_append_tv(l, &item->li_tv);
|
||||
item = item->li_next;
|
||||
// Put the rest of the list (may be empty) in the var
|
||||
// after ';'. Create a new list for this.
|
||||
l = list_alloc();
|
||||
if (l == NULL)
|
||||
return FAIL;
|
||||
|
||||
// list
|
||||
while (item != NULL)
|
||||
{
|
||||
list_append_tv(l, &item->li_tv);
|
||||
item = item->li_next;
|
||||
}
|
||||
|
||||
ltv.v_type = VAR_LIST;
|
||||
ltv.v_lock = 0;
|
||||
ltv.vval.v_list = l;
|
||||
l->lv_refcount = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
tuple_T *new_tuple = tuple_alloc();
|
||||
if (new_tuple == NULL)
|
||||
return FAIL;
|
||||
|
||||
// Put the rest of the tuple (may be empty) in the var
|
||||
// after ';'. Create a new tuple for this.
|
||||
while (idx < TUPLE_LEN(tuple))
|
||||
{
|
||||
typval_T new_tv;
|
||||
|
||||
copy_tv(TUPLE_ITEM(tuple, idx), &new_tv);
|
||||
if (tuple_append_tv(new_tuple, &new_tv) == FAIL)
|
||||
return FAIL;
|
||||
idx++;
|
||||
}
|
||||
|
||||
ltv.v_type = VAR_TUPLE;
|
||||
ltv.v_lock = 0;
|
||||
ltv.vval.v_tuple = new_tuple;
|
||||
new_tuple->tv_refcount = 1;
|
||||
}
|
||||
|
||||
ltv.v_type = VAR_LIST;
|
||||
ltv.v_lock = 0;
|
||||
ltv.vval.v_list = l;
|
||||
l->lv_refcount = 1;
|
||||
++var_idx;
|
||||
|
||||
arg = ex_let_one(skipwhite(arg + 1), <v, FALSE,
|
||||
flags | ASSIGN_UNPACK, (char_u *)"]", op, var_idx);
|
||||
flags | ASSIGN_UNPACK, (char_u *)"]", op, var_idx);
|
||||
clear_tv(<v);
|
||||
if (arg == NULL)
|
||||
return FAIL;
|
||||
@@ -2418,6 +2486,9 @@ item_lock(typval_T *tv, int deep, int lock, int check_refcount)
|
||||
}
|
||||
}
|
||||
break;
|
||||
case VAR_TUPLE:
|
||||
tuple_lock(tv->vval.v_tuple, deep, lock, check_refcount);
|
||||
break;
|
||||
case VAR_DICT:
|
||||
if ((d = tv->vval.v_dict) != NULL
|
||||
&& !(check_refcount && d->dv_refcount > 1))
|
||||
@@ -3189,9 +3260,9 @@ eval_variable(
|
||||
}
|
||||
}
|
||||
|
||||
// If a list or dict variable wasn't initialized and has meaningful
|
||||
// type, do it now. Not for global variables, they are not
|
||||
// declared.
|
||||
// If a list or tuple or dict variable wasn't initialized and has
|
||||
// meaningful type, do it now. Not for global variables, they are
|
||||
// not declared.
|
||||
if (ht != &globvarht)
|
||||
{
|
||||
if (tv->v_type == VAR_DICT && tv->vval.v_dict == NULL
|
||||
@@ -3220,6 +3291,19 @@ eval_variable(
|
||||
sv->sv_flags |= SVFLAG_ASSIGNED;
|
||||
}
|
||||
}
|
||||
else if (tv->v_type == VAR_TUPLE && tv->vval.v_tuple == NULL
|
||||
&& ((type != NULL && !was_assigned)
|
||||
|| !in_vim9script()))
|
||||
{
|
||||
tv->vval.v_tuple = tuple_alloc();
|
||||
if (tv->vval.v_tuple != NULL)
|
||||
{
|
||||
++tv->vval.v_tuple->tv_refcount;
|
||||
tv->vval.v_tuple->tv_type = alloc_type(type);
|
||||
if (sv != NULL)
|
||||
sv->sv_flags |= SVFLAG_ASSIGNED;
|
||||
}
|
||||
}
|
||||
else if (tv->v_type == VAR_BLOB && tv->vval.v_blob == NULL
|
||||
&& ((type != NULL && !was_assigned)
|
||||
|| !in_vim9script()))
|
||||
|
||||
+5
-1
@@ -3799,8 +3799,12 @@ find_ex_command(
|
||||
if (eq != NULL)
|
||||
{
|
||||
eq = skipwhite(eq);
|
||||
if (vim_strchr((char_u *)"+-*/%", *eq) != NULL)
|
||||
if (vim_strchr((char_u *)"+-*/%.", *eq) != NULL)
|
||||
{
|
||||
if (eq[0] == '.' && eq[1] == '.')
|
||||
++eq;
|
||||
++eq;
|
||||
}
|
||||
}
|
||||
if (p == NULL || p == eap->cmd || *eq != '=')
|
||||
{
|
||||
|
||||
@@ -122,31 +122,31 @@ garbage_collect(int testing)
|
||||
// buffer-local variables
|
||||
FOR_ALL_BUFFERS(buf)
|
||||
abort = abort || set_ref_in_item(&buf->b_bufvar.di_tv, copyID,
|
||||
NULL, NULL);
|
||||
NULL, NULL, NULL);
|
||||
|
||||
// window-local variables
|
||||
FOR_ALL_TAB_WINDOWS(tp, wp)
|
||||
abort = abort || set_ref_in_item(&wp->w_winvar.di_tv, copyID,
|
||||
NULL, NULL);
|
||||
NULL, NULL, NULL);
|
||||
// window-local variables in autocmd windows
|
||||
for (int i = 0; i < AUCMD_WIN_COUNT; ++i)
|
||||
if (aucmd_win[i].auc_win != NULL)
|
||||
abort = abort || set_ref_in_item(
|
||||
&aucmd_win[i].auc_win->w_winvar.di_tv, copyID, NULL, NULL);
|
||||
&aucmd_win[i].auc_win->w_winvar.di_tv, copyID, NULL, NULL, NULL);
|
||||
#ifdef FEAT_PROP_POPUP
|
||||
FOR_ALL_POPUPWINS(wp)
|
||||
abort = abort || set_ref_in_item(&wp->w_winvar.di_tv, copyID,
|
||||
NULL, NULL);
|
||||
NULL, NULL, NULL);
|
||||
FOR_ALL_TABPAGES(tp)
|
||||
FOR_ALL_POPUPWINS_IN_TAB(tp, wp)
|
||||
abort = abort || set_ref_in_item(&wp->w_winvar.di_tv, copyID,
|
||||
NULL, NULL);
|
||||
NULL, NULL, NULL);
|
||||
#endif
|
||||
|
||||
// tabpage-local variables
|
||||
FOR_ALL_TABPAGES(tp)
|
||||
abort = abort || set_ref_in_item(&tp->tp_winvar.di_tv, copyID,
|
||||
NULL, NULL);
|
||||
NULL, NULL, NULL);
|
||||
// global variables
|
||||
abort = abort || garbage_collect_globvars(copyID);
|
||||
|
||||
@@ -269,6 +269,9 @@ free_unref_items(int copyID)
|
||||
// Go through the list of lists and free items without this copyID.
|
||||
did_free |= list_free_nonref(copyID);
|
||||
|
||||
// Go through the list of tuples and free items without this copyID.
|
||||
did_free |= tuple_free_nonref(copyID);
|
||||
|
||||
// Go through the list of objects and free items without this copyID.
|
||||
did_free |= object_free_nonref(copyID);
|
||||
|
||||
@@ -291,6 +294,7 @@ free_unref_items(int copyID)
|
||||
object_free_items(copyID);
|
||||
dict_free_items(copyID);
|
||||
list_free_items(copyID);
|
||||
tuple_free_items(copyID);
|
||||
|
||||
#ifdef FEAT_JOB_CHANNEL
|
||||
// Go through the list of jobs and free items without the copyID. This
|
||||
@@ -314,7 +318,11 @@ free_unref_items(int copyID)
|
||||
* Returns TRUE if setting references failed somehow.
|
||||
*/
|
||||
int
|
||||
set_ref_in_ht(hashtab_T *ht, int copyID, list_stack_T **list_stack)
|
||||
set_ref_in_ht(
|
||||
hashtab_T *ht,
|
||||
int copyID,
|
||||
list_stack_T **list_stack,
|
||||
tuple_stack_T **tuple_stack)
|
||||
{
|
||||
int todo;
|
||||
int abort = FALSE;
|
||||
@@ -336,8 +344,9 @@ set_ref_in_ht(hashtab_T *ht, int copyID, list_stack_T **list_stack)
|
||||
if (!HASHITEM_EMPTY(hi))
|
||||
{
|
||||
--todo;
|
||||
abort = abort || set_ref_in_item(&HI2DI(hi)->di_tv, copyID,
|
||||
&ht_stack, list_stack);
|
||||
abort = abort
|
||||
|| set_ref_in_item(&HI2DI(hi)->di_tv, copyID,
|
||||
&ht_stack, list_stack, tuple_stack);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -366,7 +375,7 @@ set_ref_in_dict(dict_T *d, int copyID)
|
||||
if (d != NULL && d->dv_copyID != copyID)
|
||||
{
|
||||
d->dv_copyID = copyID;
|
||||
return set_ref_in_ht(&d->dv_hashtab, copyID, NULL);
|
||||
return set_ref_in_ht(&d->dv_hashtab, copyID, NULL, NULL);
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
@@ -382,7 +391,7 @@ set_ref_in_list(list_T *ll, int copyID)
|
||||
if (ll != NULL && ll->lv_copyID != copyID)
|
||||
{
|
||||
ll->lv_copyID = copyID;
|
||||
return set_ref_in_list_items(ll, copyID, NULL);
|
||||
return set_ref_in_list_items(ll, copyID, NULL, NULL);
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
@@ -394,7 +403,11 @@ set_ref_in_list(list_T *ll, int copyID)
|
||||
* Returns TRUE if setting references failed somehow.
|
||||
*/
|
||||
int
|
||||
set_ref_in_list_items(list_T *l, int copyID, ht_stack_T **ht_stack)
|
||||
set_ref_in_list_items(
|
||||
list_T *l,
|
||||
int copyID,
|
||||
ht_stack_T **ht_stack,
|
||||
tuple_stack_T **tuple_stack)
|
||||
{
|
||||
listitem_T *li;
|
||||
int abort = FALSE;
|
||||
@@ -411,7 +424,7 @@ set_ref_in_list_items(list_T *l, int copyID, ht_stack_T **ht_stack)
|
||||
// list_stack.
|
||||
for (li = cur_l->lv_first; !abort && li != NULL; li = li->li_next)
|
||||
abort = abort || set_ref_in_item(&li->li_tv, copyID,
|
||||
ht_stack, &list_stack);
|
||||
ht_stack, &list_stack, tuple_stack);
|
||||
if (list_stack == NULL)
|
||||
break;
|
||||
|
||||
@@ -425,6 +438,50 @@ set_ref_in_list_items(list_T *l, int copyID, ht_stack_T **ht_stack)
|
||||
return abort;
|
||||
}
|
||||
|
||||
/*
|
||||
* Mark all lists and dicts referenced through tuple "t" with "copyID".
|
||||
* "ht_stack" is used to add hashtabs to be marked. Can be NULL.
|
||||
*
|
||||
* Returns TRUE if setting references failed somehow.
|
||||
*/
|
||||
int
|
||||
set_ref_in_tuple_items(
|
||||
tuple_T *tuple,
|
||||
int copyID,
|
||||
ht_stack_T **ht_stack,
|
||||
list_stack_T **list_stack)
|
||||
{
|
||||
int abort = FALSE;
|
||||
tuple_T *cur_t;
|
||||
tuple_stack_T *tuple_stack = NULL;
|
||||
tuple_stack_T *tempitem;
|
||||
|
||||
cur_t = tuple;
|
||||
for (;;)
|
||||
{
|
||||
// Mark each item in the tuple. If the item contains a hashtab
|
||||
// it is added to ht_stack, if it contains a list it is added to
|
||||
// list_stack.
|
||||
for (int i = 0; i < cur_t->tv_items.ga_len; i++)
|
||||
{
|
||||
typval_T *tv = ((typval_T *)cur_t->tv_items.ga_data) + i;
|
||||
abort = abort
|
||||
|| set_ref_in_item(tv, copyID,
|
||||
ht_stack, list_stack, &tuple_stack);
|
||||
}
|
||||
if (tuple_stack == NULL)
|
||||
break;
|
||||
|
||||
// take an item from the stack
|
||||
cur_t = tuple_stack->tuple;
|
||||
tempitem = tuple_stack;
|
||||
tuple_stack = tuple_stack->prev;
|
||||
free(tempitem);
|
||||
}
|
||||
|
||||
return abort;
|
||||
}
|
||||
|
||||
/*
|
||||
* Mark the partial in callback 'cb' with "copyID".
|
||||
*/
|
||||
@@ -438,7 +495,7 @@ set_ref_in_callback(callback_T *cb, int copyID)
|
||||
|
||||
tv.v_type = VAR_PARTIAL;
|
||||
tv.vval.v_partial = cb->cb_partial;
|
||||
return set_ref_in_item(&tv, copyID, NULL, NULL);
|
||||
return set_ref_in_item(&tv, copyID, NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -450,7 +507,8 @@ set_ref_in_item_dict(
|
||||
dict_T *dd,
|
||||
int copyID,
|
||||
ht_stack_T **ht_stack,
|
||||
list_stack_T **list_stack)
|
||||
list_stack_T **list_stack,
|
||||
tuple_stack_T **tuple_stack)
|
||||
{
|
||||
if (dd == NULL || dd->dv_copyID == copyID)
|
||||
return FALSE;
|
||||
@@ -458,7 +516,7 @@ set_ref_in_item_dict(
|
||||
// Didn't see this dict yet.
|
||||
dd->dv_copyID = copyID;
|
||||
if (ht_stack == NULL)
|
||||
return set_ref_in_ht(&dd->dv_hashtab, copyID, list_stack);
|
||||
return set_ref_in_ht(&dd->dv_hashtab, copyID, list_stack, tuple_stack);
|
||||
|
||||
ht_stack_T *newitem = ALLOC_ONE(ht_stack_T);
|
||||
if (newitem == NULL)
|
||||
@@ -480,7 +538,8 @@ set_ref_in_item_list(
|
||||
list_T *ll,
|
||||
int copyID,
|
||||
ht_stack_T **ht_stack,
|
||||
list_stack_T **list_stack)
|
||||
list_stack_T **list_stack,
|
||||
tuple_stack_T **tuple_stack)
|
||||
{
|
||||
if (ll == NULL || ll->lv_copyID == copyID)
|
||||
return FALSE;
|
||||
@@ -488,7 +547,7 @@ set_ref_in_item_list(
|
||||
// Didn't see this list yet.
|
||||
ll->lv_copyID = copyID;
|
||||
if (list_stack == NULL)
|
||||
return set_ref_in_list_items(ll, copyID, ht_stack);
|
||||
return set_ref_in_list_items(ll, copyID, ht_stack, tuple_stack);
|
||||
|
||||
list_stack_T *newitem = ALLOC_ONE(list_stack_T);
|
||||
if (newitem == NULL)
|
||||
@@ -501,6 +560,37 @@ set_ref_in_item_list(
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Mark the tuple "tt" with "copyID".
|
||||
* Also see set_ref_in_item().
|
||||
*/
|
||||
static int
|
||||
set_ref_in_item_tuple(
|
||||
tuple_T *tt,
|
||||
int copyID,
|
||||
ht_stack_T **ht_stack,
|
||||
list_stack_T **list_stack,
|
||||
tuple_stack_T **tuple_stack)
|
||||
{
|
||||
if (tt == NULL || tt->tv_copyID == copyID)
|
||||
return FALSE;
|
||||
|
||||
// Didn't see this tuple yet.
|
||||
tt->tv_copyID = copyID;
|
||||
if (tuple_stack == NULL)
|
||||
return set_ref_in_tuple_items(tt, copyID, ht_stack, list_stack);
|
||||
|
||||
tuple_stack_T *newitem = ALLOC_ONE(tuple_stack_T);
|
||||
if (newitem == NULL)
|
||||
return TRUE;
|
||||
|
||||
newitem->tuple = tt;
|
||||
newitem->prev = *tuple_stack;
|
||||
*tuple_stack = newitem;
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Mark the partial "pt" with "copyID".
|
||||
* Also see set_ref_in_item().
|
||||
@@ -510,7 +600,8 @@ set_ref_in_item_partial(
|
||||
partial_T *pt,
|
||||
int copyID,
|
||||
ht_stack_T **ht_stack,
|
||||
list_stack_T **list_stack)
|
||||
list_stack_T **list_stack,
|
||||
tuple_stack_T **tuple_stack)
|
||||
{
|
||||
if (pt == NULL || pt->pt_copyID == copyID)
|
||||
return FALSE;
|
||||
@@ -526,7 +617,7 @@ set_ref_in_item_partial(
|
||||
|
||||
dtv.v_type = VAR_DICT;
|
||||
dtv.vval.v_dict = pt->pt_dict;
|
||||
set_ref_in_item(&dtv, copyID, ht_stack, list_stack);
|
||||
set_ref_in_item(&dtv, copyID, ht_stack, list_stack, tuple_stack);
|
||||
}
|
||||
|
||||
if (pt->pt_obj != NULL)
|
||||
@@ -535,12 +626,12 @@ set_ref_in_item_partial(
|
||||
|
||||
objtv.v_type = VAR_OBJECT;
|
||||
objtv.vval.v_object = pt->pt_obj;
|
||||
set_ref_in_item(&objtv, copyID, ht_stack, list_stack);
|
||||
set_ref_in_item(&objtv, copyID, ht_stack, list_stack, tuple_stack);
|
||||
}
|
||||
|
||||
for (int i = 0; i < pt->pt_argc; ++i)
|
||||
abort = abort || set_ref_in_item(&pt->pt_argv[i], copyID,
|
||||
ht_stack, list_stack);
|
||||
ht_stack, list_stack, tuple_stack);
|
||||
// pt_funcstack is handled in set_ref_in_funcstacks()
|
||||
// pt_loopvars is handled in set_ref_in_loopvars()
|
||||
|
||||
@@ -557,7 +648,8 @@ set_ref_in_item_job(
|
||||
job_T *job,
|
||||
int copyID,
|
||||
ht_stack_T **ht_stack,
|
||||
list_stack_T **list_stack)
|
||||
list_stack_T **list_stack,
|
||||
tuple_stack_T **tuple_stack)
|
||||
{
|
||||
typval_T dtv;
|
||||
|
||||
@@ -569,13 +661,13 @@ set_ref_in_item_job(
|
||||
{
|
||||
dtv.v_type = VAR_CHANNEL;
|
||||
dtv.vval.v_channel = job->jv_channel;
|
||||
set_ref_in_item(&dtv, copyID, ht_stack, list_stack);
|
||||
set_ref_in_item(&dtv, copyID, ht_stack, list_stack, tuple_stack);
|
||||
}
|
||||
if (job->jv_exit_cb.cb_partial != NULL)
|
||||
{
|
||||
dtv.v_type = VAR_PARTIAL;
|
||||
dtv.vval.v_partial = job->jv_exit_cb.cb_partial;
|
||||
set_ref_in_item(&dtv, copyID, ht_stack, list_stack);
|
||||
set_ref_in_item(&dtv, copyID, ht_stack, list_stack, tuple_stack);
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
@@ -590,7 +682,8 @@ set_ref_in_item_channel(
|
||||
channel_T *ch,
|
||||
int copyID,
|
||||
ht_stack_T **ht_stack,
|
||||
list_stack_T **list_stack)
|
||||
list_stack_T **list_stack,
|
||||
tuple_stack_T **tuple_stack)
|
||||
{
|
||||
typval_T dtv;
|
||||
|
||||
@@ -602,33 +695,33 @@ set_ref_in_item_channel(
|
||||
{
|
||||
for (jsonq_T *jq = ch->ch_part[part].ch_json_head.jq_next;
|
||||
jq != NULL; jq = jq->jq_next)
|
||||
set_ref_in_item(jq->jq_value, copyID, ht_stack, list_stack);
|
||||
set_ref_in_item(jq->jq_value, copyID, ht_stack, list_stack, tuple_stack);
|
||||
for (cbq_T *cq = ch->ch_part[part].ch_cb_head.cq_next; cq != NULL;
|
||||
cq = cq->cq_next)
|
||||
if (cq->cq_callback.cb_partial != NULL)
|
||||
{
|
||||
dtv.v_type = VAR_PARTIAL;
|
||||
dtv.vval.v_partial = cq->cq_callback.cb_partial;
|
||||
set_ref_in_item(&dtv, copyID, ht_stack, list_stack);
|
||||
set_ref_in_item(&dtv, copyID, ht_stack, list_stack, tuple_stack);
|
||||
}
|
||||
if (ch->ch_part[part].ch_callback.cb_partial != NULL)
|
||||
{
|
||||
dtv.v_type = VAR_PARTIAL;
|
||||
dtv.vval.v_partial = ch->ch_part[part].ch_callback.cb_partial;
|
||||
set_ref_in_item(&dtv, copyID, ht_stack, list_stack);
|
||||
set_ref_in_item(&dtv, copyID, ht_stack, list_stack, tuple_stack);
|
||||
}
|
||||
}
|
||||
if (ch->ch_callback.cb_partial != NULL)
|
||||
{
|
||||
dtv.v_type = VAR_PARTIAL;
|
||||
dtv.vval.v_partial = ch->ch_callback.cb_partial;
|
||||
set_ref_in_item(&dtv, copyID, ht_stack, list_stack);
|
||||
set_ref_in_item(&dtv, copyID, ht_stack, list_stack, tuple_stack);
|
||||
}
|
||||
if (ch->ch_close_cb.cb_partial != NULL)
|
||||
{
|
||||
dtv.v_type = VAR_PARTIAL;
|
||||
dtv.vval.v_partial = ch->ch_close_cb.cb_partial;
|
||||
set_ref_in_item(&dtv, copyID, ht_stack, list_stack);
|
||||
set_ref_in_item(&dtv, copyID, ht_stack, list_stack, tuple_stack);
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
@@ -644,7 +737,8 @@ set_ref_in_item_class(
|
||||
class_T *cl,
|
||||
int copyID,
|
||||
ht_stack_T **ht_stack,
|
||||
list_stack_T **list_stack)
|
||||
list_stack_T **list_stack,
|
||||
tuple_stack_T **tuple_stack)
|
||||
{
|
||||
int abort = FALSE;
|
||||
|
||||
@@ -659,7 +753,7 @@ set_ref_in_item_class(
|
||||
for (int i = 0; !abort && i < cl->class_class_member_count; ++i)
|
||||
abort = abort || set_ref_in_item(
|
||||
&cl->class_members_tv[i],
|
||||
copyID, ht_stack, list_stack);
|
||||
copyID, ht_stack, list_stack, tuple_stack);
|
||||
}
|
||||
|
||||
for (int i = 0; !abort && i < cl->class_class_function_count; ++i)
|
||||
@@ -682,7 +776,8 @@ set_ref_in_item_object(
|
||||
object_T *obj,
|
||||
int copyID,
|
||||
ht_stack_T **ht_stack,
|
||||
list_stack_T **list_stack)
|
||||
list_stack_T **list_stack,
|
||||
tuple_stack_T **tuple_stack)
|
||||
{
|
||||
int abort = FALSE;
|
||||
|
||||
@@ -696,7 +791,7 @@ set_ref_in_item_object(
|
||||
for (int i = 0; !abort
|
||||
&& i < obj->obj_class->class_obj_member_count; ++i)
|
||||
abort = abort || set_ref_in_item(mtv + i, copyID,
|
||||
ht_stack, list_stack);
|
||||
ht_stack, list_stack, tuple_stack);
|
||||
|
||||
return abort;
|
||||
}
|
||||
@@ -714,7 +809,8 @@ set_ref_in_item(
|
||||
typval_T *tv,
|
||||
int copyID,
|
||||
ht_stack_T **ht_stack,
|
||||
list_stack_T **list_stack)
|
||||
list_stack_T **list_stack,
|
||||
tuple_stack_T **tuple_stack)
|
||||
{
|
||||
int abort = FALSE;
|
||||
|
||||
@@ -722,12 +818,15 @@ set_ref_in_item(
|
||||
{
|
||||
case VAR_DICT:
|
||||
return set_ref_in_item_dict(tv->vval.v_dict, copyID,
|
||||
ht_stack, list_stack);
|
||||
ht_stack, list_stack, tuple_stack);
|
||||
|
||||
case VAR_LIST:
|
||||
return set_ref_in_item_list(tv->vval.v_list, copyID,
|
||||
ht_stack, list_stack);
|
||||
ht_stack, list_stack, tuple_stack);
|
||||
|
||||
case VAR_TUPLE:
|
||||
return set_ref_in_item_tuple(tv->vval.v_tuple, copyID,
|
||||
ht_stack, list_stack, tuple_stack);
|
||||
case VAR_FUNC:
|
||||
{
|
||||
abort = set_ref_in_func(tv->vval.v_string, NULL, copyID);
|
||||
@@ -736,12 +835,12 @@ set_ref_in_item(
|
||||
|
||||
case VAR_PARTIAL:
|
||||
return set_ref_in_item_partial(tv->vval.v_partial, copyID,
|
||||
ht_stack, list_stack);
|
||||
ht_stack, list_stack, tuple_stack);
|
||||
|
||||
case VAR_JOB:
|
||||
#ifdef FEAT_JOB_CHANNEL
|
||||
return set_ref_in_item_job(tv->vval.v_job, copyID,
|
||||
ht_stack, list_stack);
|
||||
ht_stack, list_stack, tuple_stack);
|
||||
#else
|
||||
break;
|
||||
#endif
|
||||
@@ -749,18 +848,18 @@ set_ref_in_item(
|
||||
case VAR_CHANNEL:
|
||||
#ifdef FEAT_JOB_CHANNEL
|
||||
return set_ref_in_item_channel(tv->vval.v_channel, copyID,
|
||||
ht_stack, list_stack);
|
||||
ht_stack, list_stack, tuple_stack);
|
||||
#else
|
||||
break;
|
||||
#endif
|
||||
|
||||
case VAR_CLASS:
|
||||
return set_ref_in_item_class(tv->vval.v_class, copyID,
|
||||
ht_stack, list_stack);
|
||||
ht_stack, list_stack, tuple_stack);
|
||||
|
||||
case VAR_OBJECT:
|
||||
return set_ref_in_item_object(tv->vval.v_object, copyID,
|
||||
ht_stack, list_stack);
|
||||
ht_stack, list_stack, tuple_stack);
|
||||
|
||||
case VAR_UNKNOWN:
|
||||
case VAR_ANY:
|
||||
|
||||
+16
-1
@@ -549,7 +549,14 @@ EXTERN int garbage_collect_at_exit INIT(= FALSE);
|
||||
#define t_typealias (static_types[90])
|
||||
#define t_const_typealias (static_types[91])
|
||||
|
||||
EXTERN type_T static_types[92]
|
||||
#define t_tuple_any (static_types[92])
|
||||
#define t_const_tuple_any (static_types[93])
|
||||
|
||||
#define t_tuple_empty (static_types[94])
|
||||
#define t_const_tuple_empty (static_types[95])
|
||||
|
||||
|
||||
EXTERN type_T static_types[96]
|
||||
#ifdef DO_INIT
|
||||
= {
|
||||
// 0: t_unknown
|
||||
@@ -735,6 +742,14 @@ EXTERN type_T static_types[92]
|
||||
// 90: t_typealias
|
||||
{VAR_TYPEALIAS, 0, 0, TTFLAG_STATIC, NULL, NULL, NULL},
|
||||
{VAR_TYPEALIAS, 0, 0, TTFLAG_STATIC|TTFLAG_CONST, NULL, NULL, NULL},
|
||||
|
||||
// 92: t_tuple_any
|
||||
{VAR_TUPLE, -1, 0, TTFLAG_STATIC, NULL, NULL, NULL},
|
||||
{VAR_TUPLE, -1, 0, TTFLAG_STATIC|TTFLAG_CONST, NULL, NULL, NULL},
|
||||
|
||||
// 94: t_tuple_empty
|
||||
{VAR_TUPLE, 0, 0, TTFLAG_STATIC, NULL, NULL, NULL},
|
||||
{VAR_TUPLE, 0, 0, TTFLAG_STATIC|TTFLAG_CONST, NULL, NULL, NULL},
|
||||
}
|
||||
#endif
|
||||
;
|
||||
|
||||
+2
-1
@@ -6248,7 +6248,7 @@ set_ref_in_py(const int copyID)
|
||||
if (func->argc)
|
||||
for (i = 0; !abort && i < func->argc; ++i)
|
||||
abort = abort
|
||||
|| set_ref_in_item(&func->argv[i], copyID, NULL, NULL);
|
||||
|| set_ref_in_item(&func->argv[i], copyID, NULL, NULL, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6777,6 +6777,7 @@ ConvertToPyObject(typval_T *tv)
|
||||
case VAR_CLASS:
|
||||
case VAR_OBJECT:
|
||||
case VAR_TYPEALIAS:
|
||||
case VAR_TUPLE: // FIXME: Need to add support for tuple
|
||||
Py_INCREF(Py_None);
|
||||
return Py_None;
|
||||
case VAR_BOOL:
|
||||
|
||||
@@ -1087,7 +1087,7 @@ set_ref_in_job(int copyID)
|
||||
{
|
||||
tv.v_type = VAR_JOB;
|
||||
tv.vval.v_job = job;
|
||||
abort = abort || set_ref_in_item(&tv, copyID, NULL, NULL);
|
||||
abort = abort || set_ref_in_item(&tv, copyID, NULL, NULL, NULL);
|
||||
}
|
||||
return abort;
|
||||
}
|
||||
|
||||
+37
@@ -267,6 +267,7 @@ json_encode_item(garray_T *gap, typval_T *val, int copyID, int options)
|
||||
char_u *res;
|
||||
blob_T *b;
|
||||
list_T *l;
|
||||
tuple_T *tuple;
|
||||
dict_T *d;
|
||||
int i;
|
||||
|
||||
@@ -369,6 +370,42 @@ json_encode_item(garray_T *gap, typval_T *val, int copyID, int options)
|
||||
}
|
||||
break;
|
||||
|
||||
case VAR_TUPLE:
|
||||
tuple = val->vval.v_tuple;
|
||||
if (tuple == NULL)
|
||||
ga_concat(gap, (char_u *)"[]");
|
||||
else
|
||||
{
|
||||
if (tuple->tv_copyID == copyID)
|
||||
ga_concat(gap, (char_u *)"[]");
|
||||
else
|
||||
{
|
||||
int len = TUPLE_LEN(tuple);
|
||||
|
||||
tuple->tv_copyID = copyID;
|
||||
ga_append(gap, '[');
|
||||
for (i = 0; i < len && !got_int; i++)
|
||||
{
|
||||
typval_T *t_item = TUPLE_ITEM(tuple, i);
|
||||
if (json_encode_item(gap, t_item, copyID,
|
||||
options & JSON_JS) == FAIL)
|
||||
return FAIL;
|
||||
|
||||
if ((options & JSON_JS)
|
||||
&& i == len - 1
|
||||
&& t_item->v_type == VAR_SPECIAL
|
||||
&& t_item->vval.v_number == VVAL_NONE)
|
||||
// add an extra comma if the last item is v:none
|
||||
ga_append(gap, ',');
|
||||
if (i <= len - 2)
|
||||
ga_append(gap, ',');
|
||||
}
|
||||
ga_append(gap, ']');
|
||||
tuple->tv_copyID = 0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case VAR_DICT:
|
||||
d = val->vval.v_dict;
|
||||
if (d == NULL)
|
||||
|
||||
+102
-23
@@ -1520,15 +1520,13 @@ f_join(typval_T *argvars, typval_T *rettv)
|
||||
|
||||
rettv->v_type = VAR_STRING;
|
||||
|
||||
if (in_vim9script()
|
||||
&& (check_for_list_arg(argvars, 0) == FAIL
|
||||
|| check_for_opt_string_arg(argvars, 1) == FAIL))
|
||||
if (check_for_list_or_tuple_arg(argvars, 0) == FAIL
|
||||
|| check_for_opt_string_arg(argvars, 1) == FAIL)
|
||||
return;
|
||||
|
||||
if (check_for_list_arg(argvars, 0) == FAIL)
|
||||
return;
|
||||
|
||||
if (argvars[0].vval.v_list == NULL)
|
||||
if ((argvars[0].v_type == VAR_LIST && argvars[0].vval.v_list == NULL)
|
||||
|| (argvars[0].v_type == VAR_TUPLE
|
||||
&& argvars[0].vval.v_tuple == NULL))
|
||||
return;
|
||||
|
||||
if (argvars[1].v_type == VAR_UNKNOWN)
|
||||
@@ -1539,7 +1537,10 @@ f_join(typval_T *argvars, typval_T *rettv)
|
||||
if (sep != NULL)
|
||||
{
|
||||
ga_init2(&ga, sizeof(char), 80);
|
||||
list_join(&ga, argvars[0].vval.v_list, sep, TRUE, FALSE, 0);
|
||||
if (argvars[0].v_type == VAR_LIST)
|
||||
list_join(&ga, argvars[0].vval.v_list, sep, TRUE, FALSE, 0);
|
||||
else
|
||||
tuple_join(&ga, argvars[0].vval.v_tuple, sep, TRUE, FALSE, 0);
|
||||
ga_append(&ga, NUL);
|
||||
rettv->vval.v_string = (char_u *)ga.ga_data;
|
||||
}
|
||||
@@ -1777,6 +1778,63 @@ f_list2str(typval_T *argvars, typval_T *rettv)
|
||||
rettv->vval.v_string = ga.ga_data;
|
||||
}
|
||||
|
||||
/*
|
||||
* "list2tuple()" function
|
||||
*/
|
||||
void
|
||||
f_list2tuple(typval_T *argvars, typval_T *rettv)
|
||||
{
|
||||
list_T *l;
|
||||
listitem_T *li;
|
||||
tuple_T *tuple;
|
||||
|
||||
rettv->v_type = VAR_TUPLE;
|
||||
rettv->vval.v_tuple = NULL;
|
||||
|
||||
if (check_for_list_arg(argvars, 0) == FAIL)
|
||||
return;
|
||||
|
||||
l = argvars[0].vval.v_list;
|
||||
if (l == NULL)
|
||||
return; // empty list results in empty tuple
|
||||
|
||||
CHECK_LIST_MATERIALIZE(l);
|
||||
|
||||
if (rettv_tuple_set_with_items(rettv, list_len(l)) == FAIL)
|
||||
return;
|
||||
|
||||
tuple = rettv->vval.v_tuple;
|
||||
FOR_ALL_LIST_ITEMS(l, li)
|
||||
{
|
||||
copy_tv(&li->li_tv, TUPLE_ITEM(tuple, TUPLE_LEN(tuple)));
|
||||
tuple->tv_items.ga_len++;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* "tuple2list()" function
|
||||
*/
|
||||
void
|
||||
f_tuple2list(typval_T *argvars, typval_T *rettv)
|
||||
{
|
||||
list_T *l;
|
||||
tuple_T *tuple;
|
||||
|
||||
if (rettv_list_alloc(rettv) == FAIL)
|
||||
return;
|
||||
|
||||
if (check_for_tuple_arg(argvars, 0) == FAIL)
|
||||
return;
|
||||
|
||||
tuple = argvars[0].vval.v_tuple;
|
||||
if (tuple == NULL)
|
||||
return; // empty tuple results in empty list
|
||||
|
||||
l = rettv->vval.v_list;
|
||||
for (int i = 0; i < tuple_len(tuple); i++)
|
||||
list_append_tv(l, TUPLE_ITEM(tuple, i));
|
||||
}
|
||||
|
||||
/*
|
||||
* Remove item argvars[1] from List argvars[0]. If argvars[2] is supplied, then
|
||||
* remove the range of items from argvars[1] to argvars[2] (inclusive).
|
||||
@@ -2560,10 +2618,16 @@ filter_map(typval_T *argvars, typval_T *rettv, filtermap_T filtermap)
|
||||
copy_tv(&argvars[0], rettv);
|
||||
|
||||
if (in_vim9script()
|
||||
&& (check_for_list_or_dict_or_blob_or_string_arg(argvars, 0)
|
||||
&& (check_for_list_tuple_dict_blob_or_string_arg(argvars, 0)
|
||||
== FAIL))
|
||||
return;
|
||||
|
||||
if (argvars[0].v_type == VAR_TUPLE && filtermap != FILTERMAP_FOREACH)
|
||||
{
|
||||
semsg(_(e_cannot_use_tuple_with_function_str), func_name);
|
||||
return;
|
||||
}
|
||||
|
||||
if (filtermap == FILTERMAP_MAP && in_vim9script())
|
||||
{
|
||||
// Check that map() does not change the declared type of the list or
|
||||
@@ -2577,11 +2641,17 @@ filter_map(typval_T *argvars, typval_T *rettv, filtermap_T filtermap)
|
||||
|
||||
if (argvars[0].v_type != VAR_BLOB
|
||||
&& argvars[0].v_type != VAR_LIST
|
||||
&& argvars[0].v_type != VAR_TUPLE
|
||||
&& argvars[0].v_type != VAR_DICT
|
||||
&& argvars[0].v_type != VAR_STRING)
|
||||
{
|
||||
semsg(_(e_argument_of_str_must_be_list_string_dictionary_or_blob),
|
||||
func_name);
|
||||
char *msg;
|
||||
|
||||
if (filtermap == FILTERMAP_FOREACH)
|
||||
msg = e_argument_of_str_must_be_list_tuple_string_dictionary_or_blob;
|
||||
else
|
||||
msg = e_argument_of_str_must_be_list_string_dictionary_or_blob;
|
||||
semsg(_(msg), func_name);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -2611,6 +2681,8 @@ filter_map(typval_T *argvars, typval_T *rettv, filtermap_T filtermap)
|
||||
arg_errmsg, rettv);
|
||||
else if (argvars[0].v_type == VAR_STRING)
|
||||
string_filter_map(tv_get_string(&argvars[0]), filtermap, expr, rettv);
|
||||
else if (argvars[0].v_type == VAR_TUPLE)
|
||||
tuple_foreach(argvars[0].vval.v_tuple, filtermap, expr);
|
||||
else // argvars[0].v_type == VAR_LIST
|
||||
list_filter_map(argvars[0].vval.v_list, filtermap, type, func_name,
|
||||
arg_errmsg, expr, rettv);
|
||||
@@ -2743,7 +2815,7 @@ f_count(typval_T *argvars, typval_T *rettv)
|
||||
int error = FALSE;
|
||||
|
||||
if (in_vim9script()
|
||||
&& (check_for_string_or_list_or_dict_arg(argvars, 0) == FAIL
|
||||
&& (check_for_string_list_tuple_or_dict_arg(argvars, 0) == FAIL
|
||||
|| check_for_opt_bool_arg(argvars, 2) == FAIL
|
||||
|| (argvars[2].v_type != VAR_UNKNOWN
|
||||
&& check_for_opt_number_arg(argvars, 3) == FAIL)))
|
||||
@@ -2765,6 +2837,16 @@ f_count(typval_T *argvars, typval_T *rettv)
|
||||
if (!error)
|
||||
n = list_count(argvars[0].vval.v_list, &argvars[1], idx, ic);
|
||||
}
|
||||
else if (!error && argvars[0].v_type == VAR_TUPLE)
|
||||
{
|
||||
long idx = 0;
|
||||
|
||||
if (argvars[2].v_type != VAR_UNKNOWN
|
||||
&& argvars[3].v_type != VAR_UNKNOWN)
|
||||
idx = (long)tv_get_number_chk(&argvars[3], &error);
|
||||
if (!error)
|
||||
n = tuple_count(argvars[0].vval.v_tuple, &argvars[1], idx, ic);
|
||||
}
|
||||
else if (!error && argvars[0].v_type == VAR_DICT)
|
||||
{
|
||||
if (argvars[2].v_type != VAR_UNKNOWN
|
||||
@@ -3033,7 +3115,7 @@ list_reverse(list_T *l, typval_T *rettv)
|
||||
void
|
||||
f_reverse(typval_T *argvars, typval_T *rettv)
|
||||
{
|
||||
if (check_for_string_or_list_or_blob_arg(argvars, 0) == FAIL)
|
||||
if (check_for_string_or_list_or_tuple_or_blob_arg(argvars, 0) == FAIL)
|
||||
return;
|
||||
|
||||
if (argvars[0].v_type == VAR_BLOB)
|
||||
@@ -3048,6 +3130,8 @@ f_reverse(typval_T *argvars, typval_T *rettv)
|
||||
}
|
||||
else if (argvars[0].v_type == VAR_LIST)
|
||||
list_reverse(argvars[0].vval.v_list, rettv);
|
||||
else if (argvars[0].v_type == VAR_TUPLE)
|
||||
tuple_reverse(argvars[0].vval.v_tuple, rettv);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -3153,6 +3237,7 @@ list_reduce(
|
||||
|
||||
/*
|
||||
* "reduce(list, { accumulator, element -> value } [, initial])" function
|
||||
* "reduce(tuple, { accumulator, element -> value } [, initial])" function
|
||||
* "reduce(blob, { accumulator, element -> value } [, initial])"
|
||||
* "reduce(string, { accumulator, element -> value } [, initial])"
|
||||
*/
|
||||
@@ -3161,18 +3246,9 @@ f_reduce(typval_T *argvars, typval_T *rettv)
|
||||
{
|
||||
char_u *func_name;
|
||||
|
||||
if (in_vim9script()
|
||||
&& check_for_string_or_list_or_blob_arg(argvars, 0) == FAIL)
|
||||
if (check_for_string_or_list_or_tuple_or_blob_arg(argvars, 0) == FAIL)
|
||||
return;
|
||||
|
||||
if (argvars[0].v_type != VAR_STRING
|
||||
&& argvars[0].v_type != VAR_LIST
|
||||
&& argvars[0].v_type != VAR_BLOB)
|
||||
{
|
||||
emsg(_(e_string_list_or_blob_required));
|
||||
return;
|
||||
}
|
||||
|
||||
if (argvars[1].v_type == VAR_FUNC)
|
||||
func_name = argvars[1].vval.v_string;
|
||||
else if (argvars[1].v_type == VAR_PARTIAL)
|
||||
@@ -3187,6 +3263,8 @@ f_reduce(typval_T *argvars, typval_T *rettv)
|
||||
|
||||
if (argvars[0].v_type == VAR_LIST)
|
||||
list_reduce(argvars, &argvars[1], rettv);
|
||||
else if (argvars[0].v_type == VAR_TUPLE)
|
||||
tuple_reduce(argvars, &argvars[1], rettv);
|
||||
else if (argvars[0].v_type == VAR_STRING)
|
||||
string_reduce(argvars, &argvars[1], rettv);
|
||||
else
|
||||
@@ -3202,6 +3280,7 @@ f_slice(typval_T *argvars, typval_T *rettv)
|
||||
if (in_vim9script()
|
||||
&& ((argvars[0].v_type != VAR_STRING
|
||||
&& argvars[0].v_type != VAR_LIST
|
||||
&& argvars[0].v_type != VAR_TUPLE
|
||||
&& argvars[0].v_type != VAR_BLOB
|
||||
&& check_for_list_arg(argvars, 0) == FAIL)
|
||||
|| check_for_number_arg(argvars, 1) == FAIL
|
||||
|
||||
@@ -487,3 +487,7 @@
|
||||
// Iterate over all the items in a hash table
|
||||
#define FOR_ALL_HASHTAB_ITEMS(ht, hi, todo) \
|
||||
for ((hi) = (ht)->ht_array; (todo) > 0; ++(hi))
|
||||
|
||||
#define TUPLE_LEN(t) (t->tv_items.ga_len)
|
||||
#define TUPLE_ITEM(t, i) \
|
||||
(((typval_T *)t->tv_items.ga_data) + i)
|
||||
|
||||
+1
-1
@@ -2561,7 +2561,7 @@ set_ref_in_nb_channel(int copyID)
|
||||
|
||||
tv.v_type = VAR_CHANNEL;
|
||||
tv.vval.v_channel = nb_channel;
|
||||
abort = set_ref_in_item(&tv, copyID, NULL, NULL);
|
||||
abort = set_ref_in_item(&tv, copyID, NULL, NULL, NULL);
|
||||
return abort;
|
||||
}
|
||||
#endif
|
||||
|
||||
+2
-2
@@ -4416,13 +4416,13 @@ set_ref_in_one_popup(win_T *wp, int copyID)
|
||||
{
|
||||
tv.v_type = VAR_PARTIAL;
|
||||
tv.vval.v_partial = wp->w_close_cb.cb_partial;
|
||||
abort = abort || set_ref_in_item(&tv, copyID, NULL, NULL);
|
||||
abort = abort || set_ref_in_item(&tv, copyID, NULL, NULL, NULL);
|
||||
}
|
||||
if (wp->w_filter_cb.cb_partial != NULL)
|
||||
{
|
||||
tv.v_type = VAR_PARTIAL;
|
||||
tv.vval.v_partial = wp->w_filter_cb.cb_partial;
|
||||
abort = abort || set_ref_in_item(&tv, copyID, NULL, NULL);
|
||||
abort = abort || set_ref_in_item(&tv, copyID, NULL, NULL, NULL);
|
||||
}
|
||||
abort = abort || set_ref_in_list(wp->w_popup_mask, copyID);
|
||||
return abort;
|
||||
|
||||
@@ -207,6 +207,7 @@ void mbyte_im_set_active(int active_arg);
|
||||
# include "textobject.pro"
|
||||
# include "textformat.pro"
|
||||
# include "time.pro"
|
||||
# include "tuple.pro"
|
||||
# include "typval.pro"
|
||||
# include "ui.pro"
|
||||
# include "undo.pro"
|
||||
|
||||
@@ -45,6 +45,7 @@ int eval0_retarg(char_u *arg, typval_T *rettv, exarg_T *eap, evalarg_T *evalarg,
|
||||
int eval1(char_u **arg, typval_T *rettv, evalarg_T *evalarg);
|
||||
void eval_addblob(typval_T *tv1, typval_T *tv2);
|
||||
int eval_addlist(typval_T *tv1, typval_T *tv2);
|
||||
int eval_addtuple(typval_T *tv1, typval_T *tv2);
|
||||
int eval_leader(char_u **arg, int vim9);
|
||||
int handle_predefined(char_u *s, int len, typval_T *rettv);
|
||||
int check_can_index(typval_T *rettv, int evaluate, int verbose);
|
||||
|
||||
@@ -23,6 +23,7 @@ void execute_common(typval_T *argvars, typval_T *rettv, int arg_off);
|
||||
void f_exists(typval_T *argvars, typval_T *rettv);
|
||||
void f_has(typval_T *argvars, typval_T *rettv);
|
||||
int dynamic_feature(char_u *feature);
|
||||
int indexof_eval_expr(typval_T *expr);
|
||||
void f_len(typval_T *argvars, typval_T *rettv);
|
||||
void mzscheme_call_vim(char_u *name, typval_T *args, typval_T *rettv);
|
||||
void range_list_materialize(list_T *list);
|
||||
|
||||
+5
-5
@@ -1,12 +1,12 @@
|
||||
/* gc.c */
|
||||
int get_copyID(void);
|
||||
int garbage_collect(int testing);
|
||||
int set_ref_in_ht(hashtab_T *ht, int copyID, list_stack_T **list_stack);
|
||||
int set_ref_in_ht(hashtab_T *ht, int copyID, list_stack_T **list_stack, tuple_stack_T **tuple_stack);
|
||||
int set_ref_in_dict(dict_T *d, int copyID);
|
||||
int set_ref_in_list(list_T *ll, int copyID);
|
||||
int set_ref_in_list_items(list_T *l, int copyID, ht_stack_T **ht_stack);
|
||||
int set_ref_in_list_items(list_T *l, int copyID, ht_stack_T **ht_stack, tuple_stack_T **tuple_stack);
|
||||
int set_ref_in_tuple_items(tuple_T *tuple, int copyID, ht_stack_T **ht_stack, list_stack_T **list_stack);
|
||||
int set_ref_in_callback(callback_T *cb, int copyID);
|
||||
int set_ref_in_item_class(class_T *cl, int copyID, ht_stack_T **ht_stack, list_stack_T **list_stack);
|
||||
int set_ref_in_item(typval_T *tv, int copyID, ht_stack_T **ht_stack, list_stack_T **list_stack);
|
||||
int set_ref_in_item_class(class_T *cl, int copyID, ht_stack_T **ht_stack, list_stack_T **list_stack, tuple_stack_T **tuple_stack);
|
||||
int set_ref_in_item(typval_T *tv, int copyID, ht_stack_T **ht_stack, list_stack_T **list_stack, tuple_stack_T **tuple_stack);
|
||||
/* vim: set ft=c : */
|
||||
|
||||
|
||||
@@ -50,6 +50,8 @@ int eval_list(char_u **arg, typval_T *rettv, evalarg_T *evalarg, int do_error);
|
||||
int write_list(FILE *fd, list_T *list, int binary);
|
||||
void init_static_list(staticList10_T *sl);
|
||||
void f_list2str(typval_T *argvars, typval_T *rettv);
|
||||
void f_list2tuple(typval_T *argvars, typval_T *rettv);
|
||||
void f_tuple2list(typval_T *argvars, typval_T *rettv);
|
||||
void f_sort(typval_T *argvars, typval_T *rettv);
|
||||
void f_uniq(typval_T *argvars, typval_T *rettv);
|
||||
int filter_map_one(typval_T *tv, typval_T *expr, filtermap_T filtermap, funccall_T *fc, typval_T *newtv, int *remp);
|
||||
|
||||
@@ -30,6 +30,7 @@ void f_test_null_list(typval_T *argvars, typval_T *rettv);
|
||||
void f_test_null_function(typval_T *argvars, typval_T *rettv);
|
||||
void f_test_null_partial(typval_T *argvars, typval_T *rettv);
|
||||
void f_test_null_string(typval_T *argvars, typval_T *rettv);
|
||||
void f_test_null_tuple(typval_T *argvars, typval_T *rettv);
|
||||
void f_test_unknown(typval_T *argvars, typval_T *rettv);
|
||||
void f_test_void(typval_T *argvars, typval_T *rettv);
|
||||
void f_test_setmouse(typval_T *argvars, typval_T *rettv);
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
/* tuple.c */
|
||||
tuple_T *tuple_alloc(void);
|
||||
tuple_T *tuple_alloc_with_items(int count);
|
||||
void tuple_set_item(tuple_T *tuple, int idx, typval_T *tv);
|
||||
int rettv_tuple_alloc(typval_T *rettv);
|
||||
void rettv_tuple_set(typval_T *rettv, tuple_T *tuple);
|
||||
int rettv_tuple_set_with_items(typval_T *rettv, int count);
|
||||
void tuple_unref(tuple_T *tuple);
|
||||
int tuple_free_nonref(int copyID);
|
||||
void tuple_free_items(int copyID);
|
||||
void tuple_free(tuple_T *tuple);
|
||||
long tuple_len(tuple_T *tuple);
|
||||
int tuple_equal(tuple_T *t1, tuple_T *t2, int ic);
|
||||
typval_T *tuple_find(tuple_T *tuple, long n);
|
||||
int tuple_append_tv(tuple_T *tuple, typval_T *tv);
|
||||
int tuple_concat(tuple_T *t1, tuple_T *t2, typval_T *tv);
|
||||
tuple_T *tuple_slice(tuple_T *tuple, long n1, long n2);
|
||||
int tuple_slice_or_index(tuple_T *tuple, int range, varnumber_T n1_arg, varnumber_T n2_arg, int exclusive, typval_T *rettv, int verbose);
|
||||
tuple_T *tuple_copy(tuple_T *orig, int deep, int top, int copyID);
|
||||
int eval_tuple(char_u **arg, typval_T *rettv, evalarg_T *evalarg, int do_error);
|
||||
void tuple_lock(tuple_T *tuple, int deep, int lock, int check_refcount);
|
||||
int tuple_join(garray_T *gap, tuple_T *tuple, char_u *sep, int echo_style, int restore_copyID, int copyID);
|
||||
char_u *tuple2string(typval_T *tv, int copyID, int restore_copyID);
|
||||
void tuple_foreach(tuple_T *tuple, filtermap_T filtermap, typval_T *expr);
|
||||
long tuple_count(tuple_T *tuple, typval_T *needle, long idx, int ic);
|
||||
void tuple2items(typval_T *argvars, typval_T *rettv);
|
||||
int index_tuple(tuple_T *tuple, typval_T *tv, int start_idx, int ic);
|
||||
int indexof_tuple(tuple_T *tuple, long startidx, typval_T *expr);
|
||||
varnumber_T tuple_max_min(tuple_T *tuple, int domax, int *error);
|
||||
void tuple_repeat(tuple_T *tuple, int n, typval_T *rettv);
|
||||
void tuple_reverse(tuple_T *tuple, typval_T *rettv);
|
||||
void tuple_reduce(typval_T *argvars, typval_T *expr, typval_T *rettv);
|
||||
int check_tuples_addable(type_T *type1, type_T *type2);
|
||||
/* vim: set ft=c : */
|
||||
@@ -18,13 +18,13 @@ int check_for_number_arg(typval_T *args, int idx);
|
||||
int check_for_opt_number_arg(typval_T *args, int idx);
|
||||
int check_for_float_or_nr_arg(typval_T *args, int idx);
|
||||
int check_for_bool_arg(typval_T *args, int idx);
|
||||
int check_for_bool_or_number_arg(typval_T *args, int idx);
|
||||
int check_for_opt_bool_arg(typval_T *args, int idx);
|
||||
int check_for_opt_bool_or_number_arg(typval_T *args, int idx);
|
||||
int check_for_blob_arg(typval_T *args, int idx);
|
||||
int check_for_list_arg(typval_T *args, int idx);
|
||||
int check_for_nonnull_list_arg(typval_T *args, int idx);
|
||||
int check_for_opt_list_arg(typval_T *args, int idx);
|
||||
int check_for_tuple_arg(typval_T *args, int idx);
|
||||
int check_for_dict_arg(typval_T *args, int idx);
|
||||
int check_for_nonnull_dict_arg(typval_T *args, int idx);
|
||||
int check_for_opt_dict_arg(typval_T *args, int idx);
|
||||
@@ -41,18 +41,20 @@ int check_for_lnum_arg(typval_T *args, int idx);
|
||||
int check_for_opt_lnum_arg(typval_T *args, int idx);
|
||||
int check_for_string_or_blob_arg(typval_T *args, int idx);
|
||||
int check_for_string_or_list_arg(typval_T *args, int idx);
|
||||
int check_for_string_or_list_or_blob_arg(typval_T *args, int idx);
|
||||
int check_for_string_or_list_or_tuple_or_blob_arg(typval_T *args, int idx);
|
||||
int check_for_opt_string_or_list_arg(typval_T *args, int idx);
|
||||
int check_for_string_or_dict_arg(typval_T *args, int idx);
|
||||
int check_for_string_or_number_or_list_arg(typval_T *args, int idx);
|
||||
int check_for_opt_string_or_number_or_list_arg(typval_T *args, int idx);
|
||||
int check_for_string_or_number_or_list_or_blob_arg(typval_T *args, int idx);
|
||||
int check_for_string_or_list_or_dict_arg(typval_T *args, int idx);
|
||||
int check_for_repeat_func_arg(typval_T *args, int idx);
|
||||
int check_for_string_list_tuple_or_dict_arg(typval_T *args, int idx);
|
||||
int check_for_string_or_func_arg(typval_T *args, int idx);
|
||||
int check_for_list_or_blob_arg(typval_T *args, int idx);
|
||||
int check_for_list_or_dict_arg(typval_T *args, int idx);
|
||||
int check_for_list_or_tuple_arg(typval_T *args, int idx);
|
||||
int check_for_list_or_tuple_or_blob_arg(typval_T *args, int idx);
|
||||
int check_for_list_or_tuple_or_dict_arg(typval_T *args, int idx);
|
||||
int check_for_list_or_dict_or_blob_arg(typval_T *args, int idx);
|
||||
int check_for_list_or_dict_or_blob_or_string_arg(typval_T *args, int idx);
|
||||
int check_for_list_tuple_dict_blob_or_string_arg(typval_T *args, int idx);
|
||||
int check_for_opt_buffer_or_dict_arg(typval_T *args, int idx);
|
||||
int check_for_object_arg(typval_T *args, int idx);
|
||||
int check_for_class_or_typealias_args(typval_T *args, int idx);
|
||||
@@ -67,6 +69,7 @@ int tv_check_lock(typval_T *tv, char_u *name, int use_gettext);
|
||||
void copy_tv(typval_T *from, typval_T *to);
|
||||
int typval_compare(typval_T *tv1, typval_T *tv2, exprtype_T type, int ic);
|
||||
int typval_compare_list(typval_T *tv1, typval_T *tv2, exprtype_T type, int ic, int *res);
|
||||
int typval_compare_tuple(typval_T *tv1, typval_T *tv2, exprtype_T type, int ic, int *res);
|
||||
int typval_compare_null(typval_T *tv1, typval_T *tv2);
|
||||
int typval_compare_blob(typval_T *tv1, typval_T *tv2, exprtype_T type, int *res);
|
||||
int typval_compare_object(typval_T *tv1, typval_T *tv2, exprtype_T type, int ic, int *res);
|
||||
|
||||
@@ -44,6 +44,7 @@ int generate_LOCKCONST(cctx_T *cctx);
|
||||
int generate_OLDSCRIPT(cctx_T *cctx, isntype_T isn_type, char_u *name, int sid, type_T *type);
|
||||
int generate_VIM9SCRIPT(cctx_T *cctx, isntype_T isn_type, int sid, int idx, type_T *type);
|
||||
int generate_NEWLIST(cctx_T *cctx, int count, int use_null);
|
||||
int generate_NEWTUPLE(cctx_T *cctx, int count, int use_null);
|
||||
int generate_NEWDICT(cctx_T *cctx, int count, int use_null);
|
||||
int generate_FUNCREF(cctx_T *cctx, ufunc_T *ufunc, class_T *cl, int object_method, int fi, int *isn_idx);
|
||||
int generate_NEWFUNC(cctx_T *cctx, char_u *lambda_name, char_u *func_name);
|
||||
|
||||
@@ -7,6 +7,7 @@ type_T *alloc_type(type_T *type);
|
||||
void free_type(type_T *type);
|
||||
void set_tv_type(typval_T *tv, type_T *type);
|
||||
type_T *get_list_type(type_T *member_type, garray_T *type_gap);
|
||||
type_T *get_tuple_type(garray_T *tuple_types_gap, garray_T *type_gap);
|
||||
type_T *get_dict_type(type_T *member_type, garray_T *type_gap);
|
||||
type_T *alloc_func_type(type_T *ret_type, int argcount, garray_T *type_gap);
|
||||
type_T *get_func_type(type_T *ret_type, int argcount, garray_T *type_gap);
|
||||
@@ -27,12 +28,14 @@ char_u *skip_type(char_u *start, int optional);
|
||||
type_T *parse_type(char_u **arg, garray_T *type_gap, int give_error);
|
||||
int equal_type(type_T *type1, type_T *type2, int flags);
|
||||
void common_type(type_T *type1, type_T *type2, type_T **dest, garray_T *type_gap);
|
||||
type_T *get_item_type(type_T *type);
|
||||
int push_type_stack(cctx_T *cctx, type_T *type);
|
||||
int push_type_stack2(cctx_T *cctx, type_T *type, type_T *decl_type);
|
||||
void set_type_on_stack(cctx_T *cctx, type_T *type, int offset);
|
||||
type_T *get_type_on_stack(cctx_T *cctx, int offset);
|
||||
type_T *get_decl_type_on_stack(cctx_T *cctx, int offset);
|
||||
type_T *get_member_type_from_stack(int count, int skip, cctx_T *cctx);
|
||||
int get_tuple_type_from_stack(int count, garray_T *tuple_types_gap, cctx_T *cctx);
|
||||
char *vartype_name(vartype_T type);
|
||||
char *type_name(type_T *type, char **tofree);
|
||||
void f_typename(typval_T *argvars, typval_T *rettv);
|
||||
|
||||
+2
-2
@@ -8065,7 +8065,7 @@ static int mark_quickfix_user_data(qf_info_T *qi, int copyID)
|
||||
typval_T* user_data = &qfp->qf_user_data;
|
||||
if (user_data != NULL && user_data->v_type != VAR_NUMBER
|
||||
&& user_data->v_type != VAR_STRING && user_data->v_type != VAR_FLOAT)
|
||||
abort = abort || set_ref_in_item(user_data, copyID, NULL, NULL);
|
||||
abort = abort || set_ref_in_item(user_data, copyID, NULL, NULL, NULL);
|
||||
}
|
||||
}
|
||||
return abort;
|
||||
@@ -8088,7 +8088,7 @@ mark_quickfix_ctx(qf_info_T *qi, int copyID)
|
||||
ctx = qi->qf_lists[i].qf_ctx;
|
||||
if (ctx != NULL && ctx->v_type != VAR_NUMBER
|
||||
&& ctx->v_type != VAR_STRING && ctx->v_type != VAR_FLOAT)
|
||||
abort = abort || set_ref_in_item(ctx, copyID, NULL, NULL);
|
||||
abort = abort || set_ref_in_item(ctx, copyID, NULL, NULL, NULL);
|
||||
|
||||
cb = &qi->qf_lists[i].qf_qftf_cb;
|
||||
abort = abort || set_ref_in_callback(cb, copyID);
|
||||
|
||||
+31
-1
@@ -73,6 +73,7 @@ typedef struct listvar_S list_T;
|
||||
typedef struct dictvar_S dict_T;
|
||||
typedef struct partial_S partial_T;
|
||||
typedef struct blobvar_S blob_T;
|
||||
typedef struct tuplevar_S tuple_T;
|
||||
|
||||
typedef struct window_S win_T;
|
||||
typedef struct wininfo_S wininfo_T;
|
||||
@@ -1511,7 +1512,8 @@ typedef enum
|
||||
VAR_INSTR, // "v_instr" is used
|
||||
VAR_CLASS, // "v_class" is used (also used for interface)
|
||||
VAR_OBJECT, // "v_object" is used
|
||||
VAR_TYPEALIAS // "v_typealias" is used
|
||||
VAR_TYPEALIAS, // "v_typealias" is used
|
||||
VAR_TUPLE // "v_tuple" is used
|
||||
} vartype_T;
|
||||
|
||||
// A type specification.
|
||||
@@ -1679,6 +1681,7 @@ struct typval_S
|
||||
class_T *v_class; // class value (can be NULL)
|
||||
object_T *v_object; // object value (can be NULL)
|
||||
typealias_T *v_typealias; // user-defined type name
|
||||
tuple_T *v_tuple; // tuple
|
||||
} vval;
|
||||
};
|
||||
|
||||
@@ -1818,6 +1821,21 @@ struct blobvar_S
|
||||
char bv_lock; // zero, VAR_LOCKED, VAR_FIXED
|
||||
};
|
||||
|
||||
/*
|
||||
* Structure to hold info about a tuple.
|
||||
*/
|
||||
struct tuplevar_S
|
||||
{
|
||||
garray_T tv_items; // tuple items
|
||||
type_T *tv_type; // current type, allocated by alloc_type()
|
||||
tuple_T *tv_copytuple; // copied tuple used by deepcopy()
|
||||
tuple_T *tv_used_next; // next tuple in used tuples list
|
||||
tuple_T *tv_used_prev; // previous tuple in used tuples list
|
||||
int tv_refcount; // reference count
|
||||
int tv_copyID; // ID used by deepcopy()
|
||||
char tv_lock; // zero, VAR_LOCKED, VAR_FIXED
|
||||
};
|
||||
|
||||
typedef int (*cfunc_T)(int argcount, typval_T *argvars, typval_T *rettv, void *state);
|
||||
typedef void (*cfunc_free_T)(void *state);
|
||||
|
||||
@@ -1846,6 +1864,8 @@ typedef struct
|
||||
blob_T *fi_blob; // blob being used
|
||||
char_u *fi_string; // copy of string being used
|
||||
int fi_byte_idx; // byte index in fi_string
|
||||
tuple_T *fi_tuple; // tuple being used
|
||||
int fi_tuple_idx; // tuple index in fi_tuple
|
||||
int fi_cs_flags; // cs_flags or'ed together
|
||||
} forinfo_T;
|
||||
|
||||
@@ -2820,6 +2840,15 @@ typedef struct list_stack_S
|
||||
struct list_stack_S *prev;
|
||||
} list_stack_T;
|
||||
|
||||
/*
|
||||
* structure used for explicit stack while garbage collecting tuples
|
||||
*/
|
||||
typedef struct tuple_stack_S
|
||||
{
|
||||
tuple_T *tuple;
|
||||
struct tuple_stack_S *prev;
|
||||
} tuple_stack_T;
|
||||
|
||||
/*
|
||||
* Structure used for iterating over dictionary items.
|
||||
* Initialize with dict_iterate_start().
|
||||
@@ -4692,6 +4721,7 @@ typedef struct lval_S
|
||||
char_u *ll_newkey; // New key for Dict in alloc. mem or NULL.
|
||||
type_T *ll_valtype; // type expected for the value or NULL
|
||||
blob_T *ll_blob; // The Blob or NULL
|
||||
tuple_T *ll_tuple; // tuple or NULL
|
||||
ufunc_T *ll_ufunc; // The function or NULL
|
||||
object_T *ll_object; // The object or NULL, class is not NULL
|
||||
class_T *ll_class; // The class or NULL, object may be NULL
|
||||
|
||||
+1
-1
@@ -5081,7 +5081,7 @@ set_ref_in_term(int copyID)
|
||||
{
|
||||
tv.v_type = VAR_JOB;
|
||||
tv.vval.v_job = term->tl_job;
|
||||
abort = abort || set_ref_in_item(&tv, copyID, NULL, NULL);
|
||||
abort = abort || set_ref_in_item(&tv, copyID, NULL, NULL, NULL);
|
||||
}
|
||||
return abort;
|
||||
}
|
||||
|
||||
@@ -327,6 +327,7 @@ NEW_TESTS = \
|
||||
test_timers \
|
||||
test_true_false \
|
||||
test_trycatch \
|
||||
test_tuple \
|
||||
test_undo \
|
||||
test_unlet \
|
||||
test_user_func \
|
||||
@@ -578,6 +579,7 @@ NEW_TESTS_RES = \
|
||||
test_timers.res \
|
||||
test_true_false.res \
|
||||
test_trycatch.res \
|
||||
test_tuple.res \
|
||||
test_undo.res \
|
||||
test_user_func.res \
|
||||
test_usercommands.res \
|
||||
|
||||
@@ -35,7 +35,7 @@ func Test_blob_create()
|
||||
call assert_fails('VAR b = 0z1.1')
|
||||
call assert_fails('VAR b = 0z.')
|
||||
call assert_fails('VAR b = 0z001122.')
|
||||
call assert_fails('call get("", 1)', 'E896:')
|
||||
call assert_fails('call get("", 1)', 'E1531:')
|
||||
call assert_equal(0, len(test_null_blob()))
|
||||
call assert_equal(0z, copy(test_null_blob()))
|
||||
END
|
||||
@@ -786,6 +786,7 @@ endfunc
|
||||
|
||||
func Test_blob_repeat()
|
||||
call assert_equal(0z, repeat(0z00, 0))
|
||||
call assert_equal(0z, repeat(0z, 1))
|
||||
call assert_equal(0z00, repeat(0z00, 1))
|
||||
call assert_equal(0z0000, repeat(0z00, 2))
|
||||
call assert_equal(0z00000000, repeat(0z0000, 2))
|
||||
|
||||
@@ -124,9 +124,9 @@ func Test_E963()
|
||||
endfunc
|
||||
|
||||
func Test_for_invalid()
|
||||
call assert_fails("for x in 99", 'E1098:')
|
||||
call assert_fails("for x in function('winnr')", 'E1098:')
|
||||
call assert_fails("for x in {'a': 9}", 'E1098:')
|
||||
call assert_fails("for x in 99", 'E1523:')
|
||||
call assert_fails("for x in function('winnr')", 'E1523:')
|
||||
call assert_fails("for x in {'a': 9}", 'E1523:')
|
||||
|
||||
let lines =<< trim END
|
||||
for v:maxcol in range(5)
|
||||
|
||||
@@ -658,10 +658,10 @@ func Test_printf_spec_b()
|
||||
endfunc
|
||||
|
||||
func Test_max_min_errors()
|
||||
call v9.CheckLegacyAndVim9Failure(['call max(v:true)'], ['E712:', 'E1013:', 'E1227:'])
|
||||
call v9.CheckLegacyAndVim9Failure(['call max(v:true)'], ['max()', 'E1013:', 'E1227:'])
|
||||
call v9.CheckLegacyAndVim9Failure(['call min(v:true)'], ['E712:', 'E1013:', 'E1227:'])
|
||||
call v9.CheckLegacyAndVim9Failure(['call min(v:true)'], ['min()', 'E1013:', 'E1227:'])
|
||||
call v9.CheckLegacyAndVim9Failure(['call max(v:true)'], ['E712:', 'E1013:', 'E1530:'])
|
||||
call v9.CheckLegacyAndVim9Failure(['call max(v:true)'], ['max()', 'E1013:', 'E1530:'])
|
||||
call v9.CheckLegacyAndVim9Failure(['call min(v:true)'], ['E712:', 'E1013:', 'E1530:'])
|
||||
call v9.CheckLegacyAndVim9Failure(['call min(v:true)'], ['min()', 'E1013:', 'E1530:'])
|
||||
endfunc
|
||||
|
||||
func Test_function_with_funcref()
|
||||
|
||||
@@ -173,6 +173,7 @@ func Test_map_filter_fails()
|
||||
call assert_fails("let l = filter([1, 2], {a, b, c -> 1})", 'E119:')
|
||||
call assert_fails('call foreach([1], "xyzzy")', 'E492:')
|
||||
call assert_fails('call foreach([1], "let a = foo")', 'E121:')
|
||||
call assert_fails('call foreach(test_null_function(), "")', 'E1525:')
|
||||
endfunc
|
||||
|
||||
func Test_map_and_modify()
|
||||
|
||||
@@ -362,7 +362,7 @@ endfunc
|
||||
|
||||
func Test_lambda_error()
|
||||
" This was causing a crash
|
||||
call assert_fails('ec{@{->{d->()()', 'E15:')
|
||||
call assert_fails('ec{@{->{d->()()', 'E451:')
|
||||
endfunc
|
||||
|
||||
func Test_closure_error()
|
||||
|
||||
@@ -310,7 +310,7 @@ func Test_let_errors()
|
||||
call assert_fails('let [a]', 'E474:')
|
||||
call assert_fails('let [a, b] = [', 'E697:')
|
||||
call assert_fails('let [a, b] = [10, 20', 'E696:')
|
||||
call assert_fails('let [a, b] = 10', 'E714:')
|
||||
call assert_fails('let [a, b] = 10', 'E1535:')
|
||||
call assert_fails('let [a, , b] = [10, 20]', 'E475:')
|
||||
call assert_fails('let [a, b&] = [10, 20]', 'E475:')
|
||||
call assert_fails('let $ = 10', 'E475:')
|
||||
|
||||
@@ -192,6 +192,14 @@ func Test_list_assign()
|
||||
END
|
||||
call v9.CheckLegacyAndVim9Success(lines)
|
||||
|
||||
let lines =<< trim END
|
||||
VAR [x, y] = test_null_list()
|
||||
END
|
||||
call v9.CheckLegacyAndVim9Failure(lines, [
|
||||
\ 'E714: List required',
|
||||
\ 'E1093: Expected 2 items but got 0',
|
||||
\ 'E714: List required'])
|
||||
|
||||
let d = {'abc': [1, 2, 3]}
|
||||
call assert_fails('let d.abc[0:0z10] = [10, 20]', 'E976: Using a Blob as a String')
|
||||
endfunc
|
||||
@@ -1000,7 +1008,7 @@ func Test_reverse_sort_uniq()
|
||||
END
|
||||
call v9.CheckLegacyAndVim9Success(lines)
|
||||
|
||||
call assert_fails('call reverse({})', 'E1252:')
|
||||
call assert_fails('call reverse({})', 'E1253:')
|
||||
call assert_fails('call uniq([1, 2], {x, y -> []})', 'E745:')
|
||||
call assert_fails("call sort([1, 2], function('min'), 1)", "E1206:")
|
||||
call assert_fails("call sort([1, 2], function('invalid_func'))", "E700:")
|
||||
@@ -1073,15 +1081,15 @@ func Test_reduce()
|
||||
call assert_fails("call reduce('', { acc, val -> acc + val })", 'E998: Reduce of an empty String with no initial value')
|
||||
call assert_fails("call reduce(test_null_string(), { acc, val -> acc + val })", 'E998: Reduce of an empty String with no initial value')
|
||||
|
||||
call assert_fails("call reduce({}, { acc, val -> acc + val }, 1)", 'E1098:')
|
||||
call assert_fails("call reduce(0, { acc, val -> acc + val }, 1)", 'E1098:')
|
||||
call assert_fails("call reduce({}, { acc, val -> acc + val }, 1)", 'E1253:')
|
||||
call assert_fails("call reduce(0, { acc, val -> acc + val }, 1)", 'E1253:')
|
||||
call assert_fails("call reduce([1, 2], 'Xdoes_not_exist')", 'E117:')
|
||||
call assert_fails("echo reduce(0z01, { acc, val -> 2 * acc + val }, '')", 'E1210:')
|
||||
|
||||
call assert_fails("vim9 reduce(0, (acc, val) => (acc .. val), '')", 'E1252:')
|
||||
call assert_fails("vim9 reduce({}, (acc, val) => (acc .. val), '')", 'E1252:')
|
||||
call assert_fails("vim9 reduce(0.1, (acc, val) => (acc .. val), '')", 'E1252:')
|
||||
call assert_fails("vim9 reduce(function('tr'), (acc, val) => (acc .. val), '')", 'E1252:')
|
||||
call assert_fails("vim9 reduce(0, (acc, val) => (acc .. val), '')", 'E1253:')
|
||||
call assert_fails("vim9 reduce({}, (acc, val) => (acc .. val), '')", 'E1253:')
|
||||
call assert_fails("vim9 reduce(0.1, (acc, val) => (acc .. val), '')", 'E1253:')
|
||||
call assert_fails("vim9 reduce(function('tr'), (acc, val) => (acc .. val), '')", 'E1253:')
|
||||
call assert_fails("call reduce('', { acc, val -> acc + val }, 1)", 'E1174:')
|
||||
call assert_fails("call reduce('', { acc, val -> acc + val }, {})", 'E1174:')
|
||||
call assert_fails("call reduce('', { acc, val -> acc + val }, 0.1)", 'E1174:')
|
||||
@@ -1463,7 +1471,7 @@ func Test_null_list()
|
||||
let l = test_null_list()
|
||||
call assert_equal([], extend(l, l, 0))
|
||||
call assert_equal(0, insert(test_null_list(), 2, -1))
|
||||
call assert_fails('let s = join([1, 2], [])', 'E730:')
|
||||
call assert_fails('let s = join([1, 2], [])', 'E1174:')
|
||||
call assert_fails('call remove(l, 0, 2)', 'E684:')
|
||||
call assert_fails('call insert(l, 2, -1)', 'E684:')
|
||||
call assert_fails('call extend(test_null_list(), test_null_list())', 'E1134:')
|
||||
@@ -1544,7 +1552,7 @@ func Test_indexof()
|
||||
call assert_fails('let i = indexof(l, "v:val == ''cyan''")', 'E735:')
|
||||
call assert_fails('let i = indexof(l, "color == ''cyan''")', 'E121:')
|
||||
call assert_fails('let i = indexof(l, {})', 'E1256:')
|
||||
call assert_fails('let i = indexof({}, "v:val == 2")', 'E1226:')
|
||||
call assert_fails('let i = indexof({}, "v:val == 2")', 'E1528:')
|
||||
call assert_fails('let i = indexof([], "v:val == 2", [])', 'E1206:')
|
||||
|
||||
func TestIdx(k, v)
|
||||
|
||||
@@ -52,7 +52,7 @@ func Test_dict_method()
|
||||
call assert_fails("let x = d->insert(0)", 'E899:')
|
||||
call assert_true(d->has_key('two'))
|
||||
call assert_equal([['one', 1], ['two', 2], ['three', 3]], d->items())
|
||||
call assert_fails("let x = d->join()", 'E1211:')
|
||||
call assert_fails("let x = d->join()", 'E1529:')
|
||||
call assert_equal(['one', 'two', 'three'], d->keys())
|
||||
call assert_equal(3, d->len())
|
||||
call assert_equal(#{one: 2, two: 3, three: 4}, d->map('v:val + 1'))
|
||||
@@ -62,7 +62,7 @@ func Test_dict_method()
|
||||
call assert_equal(2, d->remove("two"))
|
||||
let d.two = 2
|
||||
call assert_fails('let x = d->repeat(2)', 'E731:')
|
||||
call assert_fails('let x = d->reverse()', 'E1252:')
|
||||
call assert_fails('let x = d->reverse()', 'E1253:')
|
||||
call assert_fails('let x = d->sort()', 'E686:')
|
||||
call assert_equal("{'one': 1, 'two': 2, 'three': 3}", d->string())
|
||||
call assert_equal(v:t_dict, d->type())
|
||||
|
||||
@@ -433,4 +433,12 @@ func Test_put_inserted()
|
||||
bwipe!
|
||||
endfunc
|
||||
|
||||
func Test_put_tuple()
|
||||
new
|
||||
let t = ('a', 'b', 'c')
|
||||
put! =t
|
||||
call assert_equal(['a', 'b', 'c', ''], getline(1, '$'))
|
||||
bw!
|
||||
endfunc
|
||||
|
||||
" vim: shiftwidth=2 sts=2 expandtab
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -538,7 +538,7 @@ def Test_assign_unpack()
|
||||
var v2: number
|
||||
[v1, v2] = ''
|
||||
END
|
||||
v9.CheckDefFailure(lines, 'E1012: Type mismatch; expected list<any> but got string', 3)
|
||||
v9.CheckDefFailure(lines, 'E1535: List or Tuple required', 3)
|
||||
|
||||
lines =<< trim END
|
||||
g:values = [false, 0]
|
||||
|
||||
@@ -882,7 +882,7 @@ enddef
|
||||
def Test_count()
|
||||
count('ABC ABC ABC', 'b', true)->assert_equal(3)
|
||||
count('ABC ABC ABC', 'b', false)->assert_equal(0)
|
||||
v9.CheckSourceDefAndScriptFailure(['count(10, 1)'], 'E1225: String, List or Dictionary required for argument 1')
|
||||
v9.CheckSourceDefAndScriptFailure(['count(10, 1)'], 'E1225: String, List, Tuple or Dictionary required for argument 1')
|
||||
v9.CheckSourceDefAndScriptFailure(['count("a", [1], 2)'], ['E1013: Argument 3: type mismatch, expected bool but got number', 'E1212: Bool required for argument 3'])
|
||||
v9.CheckSourceDefAndScriptFailure(['count("a", [1], 0, "b")'], ['E1013: Argument 4: type mismatch, expected number but got string', 'E1210: Number required for argument 4'])
|
||||
count([1, 2, 2, 3], 2)->assert_equal(2)
|
||||
@@ -1530,7 +1530,7 @@ def Test_filter()
|
||||
END
|
||||
v9.CheckSourceScriptSuccess(lines)
|
||||
|
||||
v9.CheckSourceDefAndScriptFailure(['filter(1.1, "1")'], ['E1013: Argument 1: type mismatch, expected list<any> but got float', 'E1251: List, Dictionary, Blob or String required for argument 1'])
|
||||
v9.CheckSourceDefAndScriptFailure(['filter(1.1, "1")'], ['E1013: Argument 1: type mismatch, expected list<any> but got float', 'E1251: List, Tuple, Dictionary, Blob or String required for argument 1'])
|
||||
v9.CheckSourceDefAndScriptFailure(['filter([1, 2], 4)'], ['E1256: String or function required for argument 2', 'E1024: Using a Number as a String'])
|
||||
|
||||
lines =<< trim END
|
||||
@@ -1647,6 +1647,10 @@ def Test_foldtextresult()
|
||||
assert_equal('', foldtextresult('.'))
|
||||
enddef
|
||||
|
||||
def Test_foreach()
|
||||
v9.CheckSourceDefAndScriptFailure(['foreach(test_null_job(), "")'], ['E1013: Argument 1: type mismatch, expected list<any> but got job', 'E1251: List, Tuple, Dictionary, Blob or String required for argument 1'])
|
||||
enddef
|
||||
|
||||
def Test_fullcommand()
|
||||
assert_equal('next', fullcommand('n'))
|
||||
assert_equal('noremap', fullcommand('no'))
|
||||
@@ -1755,7 +1759,7 @@ def Test_garbagecollect()
|
||||
enddef
|
||||
|
||||
def Test_get()
|
||||
v9.CheckSourceDefAndScriptFailure(['get("a", 1)'], ['E1013: Argument 1: type mismatch, expected list<any> but got string', 'E896: Argument of get() must be a List, Dictionary or Blob'])
|
||||
v9.CheckSourceDefAndScriptFailure(['get("a", 1)'], ['E1013: Argument 1: type mismatch, expected list<any> but got string', 'E1531: Argument of get() must be a List, Tuple, Dictionary or Blob'])
|
||||
[3, 5, 2]->get(1)->assert_equal(5)
|
||||
[3, 5, 2]->get(3)->assert_equal(0)
|
||||
[3, 5, 2]->get(3, 9)->assert_equal(9)
|
||||
@@ -2276,7 +2280,7 @@ enddef
|
||||
|
||||
def Test_index()
|
||||
index(['a', 'b', 'a', 'B'], 'b', 2, true)->assert_equal(3)
|
||||
v9.CheckSourceDefAndScriptFailure(['index("a", "a")'], ['E1013: Argument 1: type mismatch, expected list<any> but got string', 'E1226: List or Blob required for argument 1'])
|
||||
v9.CheckSourceDefAndScriptFailure(['index("a", "a")'], ['E1013: Argument 1: type mismatch, expected list<any> but got string', 'E1528: List or Tuple or Blob required for argument 1'])
|
||||
v9.CheckSourceDefFailure(['index(["1"], 1)'], 'E1013: Argument 2: type mismatch, expected string but got number')
|
||||
v9.CheckSourceDefAndScriptFailure(['index(0z10, "b")'], ['E1013: Argument 2: type mismatch, expected number but got string', 'E1210: Number required for argument 2'])
|
||||
v9.CheckSourceDefAndScriptFailure(['index([1], 1, "c")'], ['E1013: Argument 3: type mismatch, expected number but got string', 'E1210: Number required for argument 3'])
|
||||
@@ -2539,7 +2543,7 @@ def Test_job_stop()
|
||||
enddef
|
||||
|
||||
def Test_join()
|
||||
v9.CheckSourceDefAndScriptFailure(['join("abc")'], ['E1013: Argument 1: type mismatch, expected list<any> but got string', 'E1211: List required for argument 1'])
|
||||
v9.CheckSourceDefAndScriptFailure(['join("abc")'], ['E1013: Argument 1: type mismatch, expected list<any> but got string', 'E1529: List or Tuple required for argument 1'])
|
||||
v9.CheckSourceDefAndScriptFailure(['join([], 2)'], ['E1013: Argument 2: type mismatch, expected string but got number', 'E1174: String required for argument 2'])
|
||||
join([''], '')->assert_equal('')
|
||||
enddef
|
||||
@@ -2660,9 +2664,9 @@ enddef
|
||||
|
||||
def Test_map()
|
||||
if has('channel')
|
||||
v9.CheckSourceDefAndScriptFailure(['map(test_null_channel(), "1")'], ['E1013: Argument 1: type mismatch, expected list<any> but got channel', 'E1251: List, Dictionary, Blob or String required for argument 1'])
|
||||
v9.CheckSourceDefAndScriptFailure(['map(test_null_channel(), "1")'], ['E1013: Argument 1: type mismatch, expected list<any> but got channel', 'E1251: List, Tuple, Dictionary, Blob or String required for argument 1'])
|
||||
endif
|
||||
v9.CheckSourceDefAndScriptFailure(['map(1, "1")'], ['E1013: Argument 1: type mismatch, expected list<any> but got number', 'E1251: List, Dictionary, Blob or String required for argument 1'])
|
||||
v9.CheckSourceDefAndScriptFailure(['map(1, "1")'], ['E1013: Argument 1: type mismatch, expected list<any> but got number', 'E1251: List, Tuple, Dictionary, Blob or String required for argument 1'])
|
||||
v9.CheckSourceDefAndScriptFailure(['map([1, 2], 4)'], ['E1256: String or function required for argument 2', 'E1024: Using a Number as a String'])
|
||||
|
||||
# type of dict remains dict<any> even when type of values changes
|
||||
@@ -2893,9 +2897,9 @@ enddef
|
||||
|
||||
def Test_mapnew()
|
||||
if has('channel')
|
||||
v9.CheckSourceDefAndScriptFailure(['mapnew(test_null_job(), "1")'], ['E1013: Argument 1: type mismatch, expected list<any> but got job', 'E1251: List, Dictionary, Blob or String required for argument 1'])
|
||||
v9.CheckSourceDefAndScriptFailure(['mapnew(test_null_job(), "1")'], ['E1013: Argument 1: type mismatch, expected list<any> but got job', 'E1251: List, Tuple, Dictionary, Blob or String required for argument 1'])
|
||||
endif
|
||||
v9.CheckSourceDefAndScriptFailure(['mapnew(1, "1")'], ['E1013: Argument 1: type mismatch, expected list<any> but got number', 'E1251: List, Dictionary, Blob or String required for argument 1'])
|
||||
v9.CheckSourceDefAndScriptFailure(['mapnew(1, "1")'], ['E1013: Argument 1: type mismatch, expected list<any> but got number', 'E1251: List, Tuple, Dictionary, Blob or String required for argument 1'])
|
||||
enddef
|
||||
|
||||
def Test_mapset()
|
||||
@@ -3072,7 +3076,7 @@ def Test_max()
|
||||
? [1, max([2, 3])]
|
||||
: [4, 5]
|
||||
assert_equal([4, 5], l2)
|
||||
v9.CheckSourceDefAndScriptFailure(['max(5)'], ['E1013: Argument 1: type mismatch, expected list<any> but got number', 'E1227: List or Dictionary required for argument 1'])
|
||||
v9.CheckSourceDefAndScriptFailure(['max(5)'], ['E1013: Argument 1: type mismatch, expected list<any> but got number', 'E1530: List or Tuple or Dictionary required for argument 1'])
|
||||
enddef
|
||||
|
||||
def Test_menu_info()
|
||||
@@ -3094,7 +3098,7 @@ def Test_min()
|
||||
? [1, min([2, 3])]
|
||||
: [4, 5]
|
||||
assert_equal([4, 5], l2)
|
||||
v9.CheckSourceDefAndScriptFailure(['min(5)'], ['E1013: Argument 1: type mismatch, expected list<any> but got number', 'E1227: List or Dictionary required for argument 1'])
|
||||
v9.CheckSourceDefAndScriptFailure(['min(5)'], ['E1013: Argument 1: type mismatch, expected list<any> but got number', 'E1530: List or Tuple or Dictionary required for argument 1'])
|
||||
enddef
|
||||
|
||||
def Test_mkdir()
|
||||
@@ -3453,7 +3457,7 @@ def Test_readfile()
|
||||
enddef
|
||||
|
||||
def Test_reduce()
|
||||
v9.CheckSourceDefAndScriptFailure(['reduce({a: 10}, "1")'], ['E1013: Argument 1: type mismatch, expected list<any> but got dict<number>', 'E1252: String, List or Blob required for argument 1'])
|
||||
v9.CheckSourceDefAndScriptFailure(['reduce({a: 10}, "1")'], ['E1013: Argument 1: type mismatch, expected list<any> but got dict<number>', 'E1253: String, List, Tuple or Blob required for argument 1'])
|
||||
assert_equal(6, [1, 2, 3]->reduce((r, c) => r + c, 0))
|
||||
assert_equal(11, 0z0506->reduce((r, c) => r + c, 0))
|
||||
enddef
|
||||
@@ -3616,8 +3620,8 @@ def Test_rename()
|
||||
enddef
|
||||
|
||||
def Test_repeat()
|
||||
v9.CheckSourceDefAndScriptFailure(['repeat(1.1, 2)'], ['E1013: Argument 1: type mismatch, expected string but got float', 'E1301: String, Number, List or Blob required for argument 1'])
|
||||
v9.CheckSourceDefAndScriptFailure(['repeat({a: 10}, 2)'], ['E1013: Argument 1: type mismatch, expected string but got dict<', 'E1301: String, Number, List or Blob required for argument 1'])
|
||||
v9.CheckSourceDefAndScriptFailure(['repeat(1.1, 2)'], ['E1013: Argument 1: type mismatch, expected string but got float', 'E1301: String, Number, List, Tuple or Blob required for argument 1'])
|
||||
v9.CheckSourceDefAndScriptFailure(['repeat({a: 10}, 2)'], ['E1013: Argument 1: type mismatch, expected string but got dict<', 'E1301: String, Number, List, Tuple or Blob required for argument 1'])
|
||||
var lines =<< trim END
|
||||
assert_equal('aaa', repeat('a', 3))
|
||||
assert_equal('111', repeat(1, 3))
|
||||
@@ -3638,7 +3642,7 @@ def Test_resolve()
|
||||
enddef
|
||||
|
||||
def Test_reverse()
|
||||
v9.CheckSourceDefAndScriptFailure(['reverse(10)'], ['E1013: Argument 1: type mismatch, expected list<any> but got number', 'E1252: String, List or Blob required for argument 1'])
|
||||
v9.CheckSourceDefAndScriptFailure(['reverse(10)'], ['E1013: Argument 1: type mismatch, expected list<any> but got number', 'E1253: String, List, Tuple or Blob required for argument 1'])
|
||||
enddef
|
||||
|
||||
def Test_reverse_return_type()
|
||||
|
||||
@@ -3695,4 +3695,184 @@ def Test_disassemble_using_script_local_var_in_obj_init()
|
||||
unlet g:instr
|
||||
enddef
|
||||
|
||||
" Disassemble the code generated for indexing a tuple
|
||||
def Test_disassemble_tuple_indexing()
|
||||
var lines =<< trim END
|
||||
vim9script
|
||||
def Fn(): tuple<...list<number>>
|
||||
var t = (5, 6, 7)
|
||||
var i = t[2]
|
||||
var j = t[1 : 2]
|
||||
return t
|
||||
enddef
|
||||
g:instr = execute('disassemble Fn')
|
||||
END
|
||||
v9.CheckScriptSuccess(lines)
|
||||
assert_match('<SNR>\d\+_Fn\_s*' ..
|
||||
'var t = (5, 6, 7)\_s*' ..
|
||||
'0 PUSHNR 5\_s*' ..
|
||||
'1 PUSHNR 6\_s*' ..
|
||||
'2 PUSHNR 7\_s*' ..
|
||||
'3 NEWTUPLE size 3\_s*' ..
|
||||
'4 SETTYPE tuple<number, number, number>\_s*' ..
|
||||
'5 STORE $0\_s*' ..
|
||||
'var i = t\[2\]\_s*' ..
|
||||
'6 LOAD $0\_s*' ..
|
||||
'7 PUSHNR 2\_s*' ..
|
||||
'8 TUPLEINDEX\_s*' ..
|
||||
'9 STORE $1\_s*' ..
|
||||
'var j = t\[1 : 2\]\_s*' ..
|
||||
'10 LOAD $0\_s*' ..
|
||||
'11 PUSHNR 1\_s*' ..
|
||||
'12 PUSHNR 2\_s*' ..
|
||||
'13 TUPLESLICE\_s*' ..
|
||||
'14 SETTYPE tuple<number, number, number>\_s*' ..
|
||||
'15 STORE $2\_s*' ..
|
||||
'return t\_s*' ..
|
||||
'16 LOAD $0\_s*' ..
|
||||
'17 RETURN', g:instr)
|
||||
unlet g:instr
|
||||
enddef
|
||||
|
||||
" Disassemble the code generated for assigning a tuple to a default value
|
||||
def Test_disassemble_tuple_default_value()
|
||||
var lines =<< trim END
|
||||
vim9script
|
||||
def Fn()
|
||||
var t: tuple<number>
|
||||
enddef
|
||||
g:instr = execute('disassemble Fn')
|
||||
END
|
||||
v9.CheckScriptSuccess(lines)
|
||||
assert_match('<SNR>\d\+_Fn\_s*' ..
|
||||
'var t: tuple<number>\_s*' ..
|
||||
'0 NEWTUPLE size 0\_s*' ..
|
||||
'1 SETTYPE tuple<number>\_s*' ..
|
||||
'2 STORE $0\_s*' ..
|
||||
'3 RETURN void', g:instr)
|
||||
unlet g:instr
|
||||
enddef
|
||||
|
||||
" Disassemble the code generated for comparing tuples
|
||||
def Test_disassemble_tuple_compare()
|
||||
var lines =<< trim END
|
||||
vim9script
|
||||
def Fn()
|
||||
var t1 = (1, 2)
|
||||
var t2 = t1
|
||||
var x = t1 == t2
|
||||
enddef
|
||||
g:instr = execute('disassemble Fn')
|
||||
END
|
||||
v9.CheckScriptSuccess(lines)
|
||||
assert_match('<SNR>\d\+_Fn\_s*' ..
|
||||
'var t1 = (1, 2)\_s*' ..
|
||||
'0 PUSHNR 1\_s*' ..
|
||||
'1 PUSHNR 2\_s*' ..
|
||||
'2 NEWTUPLE size 2\_s*' ..
|
||||
'3 SETTYPE tuple<number, number>\_s*' ..
|
||||
'4 STORE $0\_s*' ..
|
||||
'var t2 = t1\_s*' ..
|
||||
'5 LOAD $0\_s*' ..
|
||||
'6 SETTYPE tuple<number, number>\_s*' ..
|
||||
'7 STORE $1\_s*' ..
|
||||
'var x = t1 == t2\_s*' ..
|
||||
'8 LOAD $0\_s*' ..
|
||||
'9 LOAD $1\_s*' ..
|
||||
'10 COMPARETUPLE ==\_s*' ..
|
||||
'11 STORE $2\_s*' ..
|
||||
'12 RETURN void', g:instr)
|
||||
unlet g:instr
|
||||
enddef
|
||||
|
||||
" Disassemble the code generated for concatenating tuples
|
||||
def Test_disassemble_tuple_concatenate()
|
||||
var lines =<< trim END
|
||||
vim9script
|
||||
def Fn()
|
||||
var t1 = (1,) + (2,)
|
||||
enddef
|
||||
g:instr = execute('disassemble Fn')
|
||||
END
|
||||
v9.CheckScriptSuccess(lines)
|
||||
assert_match('<SNR>\d\+_Fn\_s*' ..
|
||||
'var t1 = (1,) + (2,)\_s*' ..
|
||||
'0 PUSHNR 1\_s*' ..
|
||||
'1 NEWTUPLE size 1\_s*' ..
|
||||
'2 PUSHNR 2\_s*' ..
|
||||
'3 NEWTUPLE size 1\_s*' ..
|
||||
'4 ADDTUPLE\_s*' ..
|
||||
'5 SETTYPE tuple<number, number>\_s*' ..
|
||||
'6 STORE $0\_s*' ..
|
||||
'7 RETURN void', g:instr)
|
||||
unlet g:instr
|
||||
enddef
|
||||
|
||||
" Disassemble the code generated for a constant tupe
|
||||
def Test_disassemble_tuple_const()
|
||||
var lines =<< trim END
|
||||
vim9script
|
||||
def Fn()
|
||||
const t = (1, 2, 3)
|
||||
var x = t[1 : 2]
|
||||
enddef
|
||||
g:instr = execute('disassemble Fn')
|
||||
END
|
||||
v9.CheckScriptSuccess(lines)
|
||||
assert_match('<SNR>\d\+_Fn\_s*' ..
|
||||
'const t = (1, 2, 3)\_s*' ..
|
||||
'0 PUSHNR 1\_s*' ..
|
||||
'1 PUSHNR 2\_s*' ..
|
||||
'2 PUSHNR 3\_s*' ..
|
||||
'3 NEWTUPLE size 3\_s*' ..
|
||||
'4 LOCKCONST\_s*' ..
|
||||
'5 SETTYPE tuple<number, number, number>\_s*' ..
|
||||
'6 STORE $0\_s*' ..
|
||||
'var x = t\[1 : 2\]\_s*' ..
|
||||
'7 LOAD $0\_s*' ..
|
||||
'8 PUSHNR 1\_s*' ..
|
||||
'9 PUSHNR 2\_s*' ..
|
||||
'10 TUPLESLICE\_s*' ..
|
||||
'11 SETTYPE tuple<number, number, number>\_s*' ..
|
||||
'12 STORE $1\_s*' ..
|
||||
'13 RETURN void', g:instr)
|
||||
unlet g:instr
|
||||
enddef
|
||||
|
||||
" Disassemble the code generated for setting the type when using a tuple in an
|
||||
" assignment
|
||||
def Test_disassemble_assign_tuple_set_type()
|
||||
var lines =<< trim END
|
||||
vim9script
|
||||
def Fn()
|
||||
var x = (1,)
|
||||
enddef
|
||||
g:instr = execute('disassemble Fn')
|
||||
END
|
||||
v9.CheckScriptSuccess(lines)
|
||||
assert_match('<SNR>\d\+_Fn\_s*' ..
|
||||
'var x = (1,)\_s*' ..
|
||||
'0 PUSHNR 1\_s*' ..
|
||||
'1 NEWTUPLE size 1\_s*' ..
|
||||
'2 SETTYPE tuple<number>\_s*' ..
|
||||
'3 STORE $0\_s*' ..
|
||||
'4 RETURN void', g:instr)
|
||||
|
||||
lines =<< trim END
|
||||
vim9script
|
||||
def Fn()
|
||||
var x = ()
|
||||
enddef
|
||||
g:instr = execute('disassemble Fn')
|
||||
END
|
||||
v9.CheckScriptSuccess(lines)
|
||||
assert_match('<SNR>\d\+_Fn\_s*' ..
|
||||
'var x = ()\_s*' ..
|
||||
'0 NEWTUPLE size 0\_s*' ..
|
||||
'1 STORE $0\_s*' ..
|
||||
'2 RETURN void', g:instr)
|
||||
|
||||
unlet g:instr
|
||||
enddef
|
||||
|
||||
" vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker
|
||||
|
||||
@@ -7519,6 +7519,16 @@ func Test_for_over_string()
|
||||
endfor
|
||||
endfunc
|
||||
|
||||
" Test for 'for' loop failures
|
||||
func Test_for_loop_failure()
|
||||
func ForFn()
|
||||
for x in test_null_job()
|
||||
endfor
|
||||
endfunc
|
||||
call assert_fails('call ForFn()', 'E1523: String, List, Tuple or Blob required')
|
||||
delfunc ForFn
|
||||
endfunc
|
||||
|
||||
" Test for deeply nested :source command {{{1
|
||||
func Test_deeply_nested_source()
|
||||
let lines =<< trim END
|
||||
|
||||
+128
-28
@@ -191,19 +191,23 @@ export func CheckLegacyFailure(lines, error)
|
||||
endtry
|
||||
endfunc
|
||||
|
||||
# Translate "lines" to legacy Vim script
|
||||
def LegacyTrans(lines: list<string>): list<string>
|
||||
return lines->mapnew((_, v) =>
|
||||
v->substitute('\<VAR\>', 'let', 'g')
|
||||
->substitute('\<LET\>', 'let', 'g')
|
||||
->substitute('\<LSTART\>', '{', 'g')
|
||||
->substitute('\<LMIDDLE\>', '->', 'g')
|
||||
->substitute('\<LEND\>', '}', 'g')
|
||||
->substitute('\<TRUE\>', '1', 'g')
|
||||
->substitute('\<FALSE\>', '0', 'g')
|
||||
->substitute('#"', ' "', 'g'))
|
||||
enddef
|
||||
|
||||
# Execute "lines" in a legacy function, translated as in
|
||||
# CheckLegacyAndVim9Success()
|
||||
export def CheckTransLegacySuccess(lines: list<string>)
|
||||
var legacylines = lines->mapnew((_, v) =>
|
||||
v->substitute('\<VAR\>', 'let', 'g')
|
||||
->substitute('\<LET\>', 'let', 'g')
|
||||
->substitute('\<LSTART\>', '{', 'g')
|
||||
->substitute('\<LMIDDLE\>', '->', 'g')
|
||||
->substitute('\<LEND\>', '}', 'g')
|
||||
->substitute('\<TRUE\>', '1', 'g')
|
||||
->substitute('\<FALSE\>', '0', 'g')
|
||||
->substitute('#"', ' "', 'g'))
|
||||
CheckLegacySuccess(legacylines)
|
||||
CheckLegacySuccess(LegacyTrans(lines))
|
||||
enddef
|
||||
|
||||
export def Vim9Trans(lines: list<string>): list<string>
|
||||
@@ -264,16 +268,87 @@ export def CheckLegacyAndVim9Failure(lines: list<string>, error: any)
|
||||
var legacylines = lines->mapnew((_, v) =>
|
||||
v->substitute('\<VAR\>', 'let', 'g')
|
||||
->substitute('\<LET\>', 'let', 'g')
|
||||
->substitute('\<LSTART\>', '{', 'g')
|
||||
->substitute('\<LMIDDLE\>', '->', 'g')
|
||||
->substitute('\<LEND\>', '}', 'g')
|
||||
->substitute('\<TRUE\>', '1', 'g')
|
||||
->substitute('\<FALSE\>', '0', 'g')
|
||||
->substitute('#"', ' "', 'g'))
|
||||
CheckLegacyFailure(legacylines, legacyError)
|
||||
|
||||
var vim9lines = lines->mapnew((_, v) =>
|
||||
v->substitute('\<VAR\>', 'var', 'g')
|
||||
->substitute('\<LET ', '', 'g'))
|
||||
->substitute('\<LET ', '', 'g')
|
||||
->substitute('\<LSTART\>', '(', 'g')
|
||||
->substitute('\<LMIDDLE\>', ') =>', 'g')
|
||||
->substitute(' *\<LEND\> *', '', 'g')
|
||||
->substitute('\<TRUE\>', 'true', 'g')
|
||||
->substitute('\<FALSE\>', 'false', 'g'))
|
||||
CheckDefExecFailure(vim9lines, defError)
|
||||
CheckScriptFailure(['vim9script'] + vim9lines, scriptError)
|
||||
enddef
|
||||
|
||||
# Check that "lines" inside a legacy function has no error.
|
||||
export func CheckSourceLegacySuccess(lines)
|
||||
let cwd = getcwd()
|
||||
new
|
||||
call setline(1, ['func Func()'] + a:lines + ['endfunc', 'call Func()'])
|
||||
let bnr = bufnr()
|
||||
try
|
||||
:source
|
||||
finally
|
||||
delfunc! Func
|
||||
call chdir(cwd)
|
||||
exe $':bw! {bnr}'
|
||||
endtry
|
||||
endfunc
|
||||
|
||||
# Check that "lines" inside a legacy function results in the expected error
|
||||
export func CheckSourceLegacyFailure(lines, error)
|
||||
let cwd = getcwd()
|
||||
new
|
||||
call setline(1, ['func Func()'] + a:lines + ['endfunc', 'call Func()'])
|
||||
let bnr = bufnr()
|
||||
try
|
||||
call assert_fails('source', a:error)
|
||||
finally
|
||||
delfunc! Func
|
||||
call chdir(cwd)
|
||||
exe $':bw! {bnr}'
|
||||
endtry
|
||||
endfunc
|
||||
|
||||
# Execute "lines" in a legacy function, translated as in
|
||||
# CheckSourceLegacyAndVim9Success()
|
||||
export def CheckSourceTransLegacySuccess(lines: list<string>)
|
||||
CheckSourceLegacySuccess(LegacyTrans(lines))
|
||||
enddef
|
||||
|
||||
# Execute "lines" in a :def function, translated as in
|
||||
# CheckLegacyAndVim9Success()
|
||||
export def CheckSourceTransDefSuccess(lines: list<string>)
|
||||
CheckSourceDefSuccess(Vim9Trans(lines))
|
||||
enddef
|
||||
|
||||
# Execute "lines" in a Vim9 script, translated as in
|
||||
# CheckLegacyAndVim9Success()
|
||||
export def CheckSourceTransVim9Success(lines: list<string>)
|
||||
CheckSourceScriptSuccess(['vim9script'] + Vim9Trans(lines))
|
||||
enddef
|
||||
|
||||
# Execute "lines" in a legacy function, :def function and Vim9 script.
|
||||
# Use 'VAR' for a declaration.
|
||||
# Use 'LET' for an assignment
|
||||
# Use ' #"' for a comment
|
||||
# Use LSTART arg LMIDDLE expr LEND for lambda
|
||||
# Use 'TRUE' for 1 in legacy, true in Vim9
|
||||
# Use 'FALSE' for 0 in legacy, false in Vim9
|
||||
export def CheckSourceLegacyAndVim9Success(lines: list<string>)
|
||||
CheckSourceTransLegacySuccess(lines)
|
||||
CheckSourceTransDefSuccess(lines)
|
||||
CheckSourceTransVim9Success(lines)
|
||||
enddef
|
||||
|
||||
# :source a list of "lines" and check whether it fails with "error"
|
||||
export def CheckSourceScriptFailure(lines: list<string>, error: string, lnum = -3)
|
||||
var cwd = getcwd()
|
||||
@@ -317,18 +392,6 @@ export def CheckSourceScriptSuccess(lines: list<string>)
|
||||
endtry
|
||||
enddef
|
||||
|
||||
export def CheckSourceSuccess(lines: list<string>)
|
||||
CheckSourceScriptSuccess(lines)
|
||||
enddef
|
||||
|
||||
export def CheckSourceFailure(lines: list<string>, error: string, lnum = -3)
|
||||
CheckSourceScriptFailure(lines, error, lnum)
|
||||
enddef
|
||||
|
||||
export def CheckSourceFailureList(lines: list<string>, errors: list<string>, lnum = -3)
|
||||
CheckSourceScriptFailureList(lines, errors, lnum)
|
||||
enddef
|
||||
|
||||
# :source a List of "lines" inside a ":def" function and check that no error
|
||||
# occurs when called.
|
||||
export func CheckSourceDefSuccess(lines)
|
||||
@@ -346,11 +409,6 @@ export func CheckSourceDefSuccess(lines)
|
||||
endtry
|
||||
endfunc
|
||||
|
||||
export def CheckSourceDefAndScriptSuccess(lines: list<string>)
|
||||
CheckSourceDefSuccess(lines)
|
||||
CheckSourceScriptSuccess(['vim9script'] + lines)
|
||||
enddef
|
||||
|
||||
# Check that "lines" inside a ":def" function has no error when compiled.
|
||||
export func CheckSourceDefCompileSuccess(lines)
|
||||
let cwd = getcwd()
|
||||
@@ -447,3 +505,45 @@ export def CheckSourceDefExecAndScriptFailure(lines: list<string>, error: any, l
|
||||
CheckSourceScriptFailure(['vim9script'] + lines, errorScript, lnum + 1)
|
||||
enddef
|
||||
|
||||
export def CheckSourceSuccess(lines: list<string>)
|
||||
CheckSourceScriptSuccess(lines)
|
||||
enddef
|
||||
|
||||
export def CheckSourceFailure(lines: list<string>, error: string, lnum = -3)
|
||||
CheckSourceScriptFailure(lines, error, lnum)
|
||||
enddef
|
||||
|
||||
export def CheckSourceFailureList(lines: list<string>, errors: list<string>, lnum = -3)
|
||||
CheckSourceScriptFailureList(lines, errors, lnum)
|
||||
enddef
|
||||
|
||||
export def CheckSourceDefAndScriptSuccess(lines: list<string>)
|
||||
CheckSourceDefSuccess(lines)
|
||||
CheckSourceScriptSuccess(['vim9script'] + lines)
|
||||
enddef
|
||||
|
||||
# Execute "lines" in a legacy function, :def function and Vim9 script.
|
||||
# Use 'VAR' for a declaration.
|
||||
# Use 'LET' for an assignment
|
||||
# Use ' #"' for a comment
|
||||
export def CheckSourceLegacyAndVim9Failure(lines: list<string>, error: any)
|
||||
var legacyError: string
|
||||
var defError: string
|
||||
var scriptError: string
|
||||
|
||||
if type(error) == type('string')
|
||||
legacyError = error
|
||||
defError = error
|
||||
scriptError = error
|
||||
else
|
||||
legacyError = error[0]
|
||||
defError = error[1]
|
||||
scriptError = error[2]
|
||||
endif
|
||||
|
||||
CheckSourceLegacyFailure(LegacyTrans(lines), legacyError)
|
||||
var vim9lines = Vim9Trans(lines)
|
||||
CheckSourceDefExecFailure(vim9lines, defError)
|
||||
CheckSourceScriptFailure(['vim9script'] + vim9lines, scriptError)
|
||||
enddef
|
||||
|
||||
|
||||
@@ -1132,6 +1132,10 @@ f_test_refcount(typval_T *argvars, typval_T *rettv)
|
||||
if (argvars[0].vval.v_list != NULL)
|
||||
retval = argvars[0].vval.v_list->lv_refcount - 1;
|
||||
break;
|
||||
case VAR_TUPLE:
|
||||
if (argvars[0].vval.v_tuple != NULL)
|
||||
retval = argvars[0].vval.v_tuple->tv_refcount - 1;
|
||||
break;
|
||||
case VAR_DICT:
|
||||
if (argvars[0].vval.v_dict != NULL)
|
||||
retval = argvars[0].vval.v_dict->dv_refcount - 1;
|
||||
@@ -1248,6 +1252,12 @@ f_test_null_string(typval_T *argvars UNUSED, typval_T *rettv)
|
||||
rettv->vval.v_string = NULL;
|
||||
}
|
||||
|
||||
void
|
||||
f_test_null_tuple(typval_T *argvars UNUSED, typval_T *rettv)
|
||||
{
|
||||
rettv_tuple_set(rettv, NULL);
|
||||
}
|
||||
|
||||
void
|
||||
f_test_unknown(typval_T *argvars UNUSED, typval_T *rettv)
|
||||
{
|
||||
|
||||
+1
-1
@@ -778,7 +778,7 @@ set_ref_in_timer(int copyID)
|
||||
tv.v_type = VAR_FUNC;
|
||||
tv.vval.v_string = timer->tr_callback.cb_name;
|
||||
}
|
||||
abort = abort || set_ref_in_item(&tv, copyID, NULL, NULL);
|
||||
abort = abort || set_ref_in_item(&tv, copyID, NULL, NULL, NULL);
|
||||
}
|
||||
return abort;
|
||||
}
|
||||
|
||||
+1122
File diff suppressed because it is too large
Load Diff
+157
-20
@@ -72,6 +72,9 @@ free_tv(typval_T *varp)
|
||||
case VAR_LIST:
|
||||
list_unref(varp->vval.v_list);
|
||||
break;
|
||||
case VAR_TUPLE:
|
||||
tuple_unref(varp->vval.v_tuple);
|
||||
break;
|
||||
case VAR_DICT:
|
||||
dict_unref(varp->vval.v_dict);
|
||||
break;
|
||||
@@ -138,6 +141,10 @@ clear_tv(typval_T *varp)
|
||||
list_unref(varp->vval.v_list);
|
||||
varp->vval.v_list = NULL;
|
||||
break;
|
||||
case VAR_TUPLE:
|
||||
tuple_unref(varp->vval.v_tuple);
|
||||
varp->vval.v_tuple = NULL;
|
||||
break;
|
||||
case VAR_DICT:
|
||||
dict_unref(varp->vval.v_dict);
|
||||
varp->vval.v_dict = NULL;
|
||||
@@ -234,6 +241,9 @@ tv_get_bool_or_number_chk(
|
||||
case VAR_LIST:
|
||||
emsg(_(e_using_list_as_number));
|
||||
break;
|
||||
case VAR_TUPLE:
|
||||
emsg(_(e_using_tuple_as_number));
|
||||
break;
|
||||
case VAR_DICT:
|
||||
emsg(_(e_using_dictionary_as_number));
|
||||
break;
|
||||
@@ -368,6 +378,9 @@ tv_get_float_chk(typval_T *varp, int *error)
|
||||
case VAR_LIST:
|
||||
emsg(_(e_using_list_as_float));
|
||||
break;
|
||||
case VAR_TUPLE:
|
||||
emsg(_(e_using_tuple_as_float));
|
||||
break;
|
||||
case VAR_DICT:
|
||||
emsg(_(e_using_dictionary_as_float));
|
||||
break;
|
||||
@@ -529,7 +542,7 @@ check_for_bool_arg(typval_T *args, int idx)
|
||||
/*
|
||||
* Give an error and return FAIL unless "args[idx]" is a bool or a number.
|
||||
*/
|
||||
int
|
||||
static int
|
||||
check_for_bool_or_number_arg(typval_T *args, int idx)
|
||||
{
|
||||
if (args[idx].v_type != VAR_BOOL && args[idx].v_type != VAR_NUMBER)
|
||||
@@ -619,6 +632,20 @@ check_for_opt_list_arg(typval_T *args, int idx)
|
||||
|| check_for_list_arg(args, idx) != FAIL) ? OK : FAIL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Give an error and return FAIL unless "args[idx]" is a tuple.
|
||||
*/
|
||||
int
|
||||
check_for_tuple_arg(typval_T *args, int idx)
|
||||
{
|
||||
if (args[idx].v_type != VAR_TUPLE)
|
||||
{
|
||||
semsg(_(e_tuple_required_for_argument_nr), idx + 1);
|
||||
return FAIL;
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* Give an error and return FAIL unless "args[idx]" is a dict.
|
||||
*/
|
||||
@@ -827,17 +854,18 @@ check_for_string_or_list_arg(typval_T *args, int idx)
|
||||
}
|
||||
|
||||
/*
|
||||
* Give an error and return FAIL unless "args[idx]" is a string, a list or a
|
||||
* blob.
|
||||
* Give an error and return FAIL unless "args[idx]" is a string, a list, a
|
||||
* tuple or a blob.
|
||||
*/
|
||||
int
|
||||
check_for_string_or_list_or_blob_arg(typval_T *args, int idx)
|
||||
check_for_string_or_list_or_tuple_or_blob_arg(typval_T *args, int idx)
|
||||
{
|
||||
if (args[idx].v_type != VAR_STRING
|
||||
&& args[idx].v_type != VAR_LIST
|
||||
&& args[idx].v_type != VAR_TUPLE
|
||||
&& args[idx].v_type != VAR_BLOB)
|
||||
{
|
||||
semsg(_(e_string_list_or_blob_required_for_argument_nr), idx + 1);
|
||||
semsg(_(e_string_list_tuple_or_blob_required_for_argument_nr), idx + 1);
|
||||
return FAIL;
|
||||
}
|
||||
return OK;
|
||||
@@ -897,35 +925,37 @@ check_for_opt_string_or_number_or_list_arg(typval_T *args, int idx)
|
||||
}
|
||||
|
||||
/*
|
||||
* Give an error and return FAIL unless "args[idx]" is a string or a number
|
||||
* or a list or a blob.
|
||||
* Give an error and return FAIL unless "args[idx]" is a string, a number, a
|
||||
* list, a tuple or a blob.
|
||||
*/
|
||||
int
|
||||
check_for_string_or_number_or_list_or_blob_arg(typval_T *args, int idx)
|
||||
check_for_repeat_func_arg(typval_T *args, int idx)
|
||||
{
|
||||
if (args[idx].v_type != VAR_STRING
|
||||
&& args[idx].v_type != VAR_NUMBER
|
||||
&& args[idx].v_type != VAR_LIST
|
||||
&& args[idx].v_type != VAR_TUPLE
|
||||
&& args[idx].v_type != VAR_BLOB)
|
||||
{
|
||||
semsg(_(e_string_number_list_or_blob_required_for_argument_nr), idx + 1);
|
||||
semsg(_(e_repeatable_type_required_for_argument_nr), idx + 1);
|
||||
return FAIL;
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* Give an error and return FAIL unless "args[idx]" is a string or a list
|
||||
* or a dict.
|
||||
* Give an error and return FAIL unless "args[idx]" is a string, a list, a
|
||||
* tuple or a dict.
|
||||
*/
|
||||
int
|
||||
check_for_string_or_list_or_dict_arg(typval_T *args, int idx)
|
||||
check_for_string_list_tuple_or_dict_arg(typval_T *args, int idx)
|
||||
{
|
||||
if (args[idx].v_type != VAR_STRING
|
||||
&& args[idx].v_type != VAR_LIST
|
||||
&& args[idx].v_type != VAR_TUPLE
|
||||
&& args[idx].v_type != VAR_DICT)
|
||||
{
|
||||
semsg(_(e_string_list_or_dict_required_for_argument_nr), idx + 1);
|
||||
semsg(_(e_string_list_tuple_or_dict_required_for_argument_nr), idx + 1);
|
||||
return FAIL;
|
||||
}
|
||||
return OK;
|
||||
@@ -963,15 +993,48 @@ check_for_list_or_blob_arg(typval_T *args, int idx)
|
||||
}
|
||||
|
||||
/*
|
||||
* Give an error and return FAIL unless "args[idx]" is a list or dict
|
||||
* Give an error and return FAIL unless "args[idx]" is a list or a tuple.
|
||||
*/
|
||||
int
|
||||
check_for_list_or_dict_arg(typval_T *args, int idx)
|
||||
check_for_list_or_tuple_arg(typval_T *args, int idx)
|
||||
{
|
||||
if (args[idx].v_type != VAR_LIST && args[idx].v_type != VAR_TUPLE)
|
||||
{
|
||||
semsg(_(e_list_or_tuple_required_for_argument_nr), idx + 1);
|
||||
return FAIL;
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* Give an error and return FAIL unless "args[idx]" is a list, a tuple or a
|
||||
* blob.
|
||||
*/
|
||||
int
|
||||
check_for_list_or_tuple_or_blob_arg(typval_T *args, int idx)
|
||||
{
|
||||
if (args[idx].v_type != VAR_LIST
|
||||
&& args[idx].v_type != VAR_TUPLE
|
||||
&& args[idx].v_type != VAR_BLOB)
|
||||
{
|
||||
semsg(_(e_list_or_tuple_or_blob_required_for_argument_nr), idx + 1);
|
||||
return FAIL;
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* Give an error and return FAIL unless "args[idx]" is a list, a tuple or a
|
||||
* dict
|
||||
*/
|
||||
int
|
||||
check_for_list_or_tuple_or_dict_arg(typval_T *args, int idx)
|
||||
{
|
||||
if (args[idx].v_type != VAR_LIST
|
||||
&& args[idx].v_type != VAR_TUPLE
|
||||
&& args[idx].v_type != VAR_DICT)
|
||||
{
|
||||
semsg(_(e_list_or_dict_required_for_argument_nr), idx + 1);
|
||||
semsg(_(e_list_or_tuple_or_dict_required_for_argument_nr), idx + 1);
|
||||
return FAIL;
|
||||
}
|
||||
return OK;
|
||||
@@ -995,18 +1058,19 @@ check_for_list_or_dict_or_blob_arg(typval_T *args, int idx)
|
||||
}
|
||||
|
||||
/*
|
||||
* Give an error and return FAIL unless "args[idx]" is a list or dict or a
|
||||
* blob or a string.
|
||||
* Give an error and return FAIL unless "args[idx]" is a list, a tuple, a dict,
|
||||
* a blob or a string.
|
||||
*/
|
||||
int
|
||||
check_for_list_or_dict_or_blob_or_string_arg(typval_T *args, int idx)
|
||||
check_for_list_tuple_dict_blob_or_string_arg(typval_T *args, int idx)
|
||||
{
|
||||
if (args[idx].v_type != VAR_LIST
|
||||
&& args[idx].v_type != VAR_TUPLE
|
||||
&& args[idx].v_type != VAR_DICT
|
||||
&& args[idx].v_type != VAR_BLOB
|
||||
&& args[idx].v_type != VAR_STRING)
|
||||
{
|
||||
semsg(_(e_list_dict_blob_or_string_required_for_argument_nr), idx + 1);
|
||||
semsg(_(e_list_tuple_dict_blob_or_string_required_for_argument_nr), idx + 1);
|
||||
return FAIL;
|
||||
}
|
||||
return OK;
|
||||
@@ -1149,6 +1213,9 @@ tv_get_string_buf_chk_strict(typval_T *varp, char_u *buf, int strict)
|
||||
case VAR_LIST:
|
||||
emsg(_(e_using_list_as_string));
|
||||
break;
|
||||
case VAR_TUPLE:
|
||||
emsg(_(e_using_tuple_as_string));
|
||||
break;
|
||||
case VAR_DICT:
|
||||
emsg(_(e_using_dictionary_as_string));
|
||||
break;
|
||||
@@ -1267,6 +1334,10 @@ tv_check_lock(typval_T *tv, char_u *name, int use_gettext)
|
||||
if (tv->vval.v_list != NULL)
|
||||
lock = tv->vval.v_list->lv_lock;
|
||||
break;
|
||||
case VAR_TUPLE:
|
||||
if (tv->vval.v_tuple != NULL)
|
||||
lock = tv->vval.v_tuple->tv_lock;
|
||||
break;
|
||||
case VAR_DICT:
|
||||
if (tv->vval.v_dict != NULL)
|
||||
lock = tv->vval.v_dict->dv_lock;
|
||||
@@ -1364,6 +1435,15 @@ copy_tv(typval_T *from, typval_T *to)
|
||||
++to->vval.v_list->lv_refcount;
|
||||
}
|
||||
break;
|
||||
case VAR_TUPLE:
|
||||
if (from->vval.v_tuple == NULL)
|
||||
to->vval.v_tuple = NULL;
|
||||
else
|
||||
{
|
||||
to->vval.v_tuple = from->vval.v_tuple;
|
||||
++to->vval.v_tuple->tv_refcount;
|
||||
}
|
||||
break;
|
||||
case VAR_DICT:
|
||||
if (from->vval.v_dict == NULL)
|
||||
to->vval.v_dict = NULL;
|
||||
@@ -1452,6 +1532,15 @@ typval_compare(
|
||||
}
|
||||
n1 = res;
|
||||
}
|
||||
else if (tv1->v_type == VAR_TUPLE || tv2->v_type == VAR_TUPLE)
|
||||
{
|
||||
if (typval_compare_tuple(tv1, tv2, type, ic, &res) == FAIL)
|
||||
{
|
||||
clear_tv(tv1);
|
||||
return FAIL;
|
||||
}
|
||||
n1 = res;
|
||||
}
|
||||
else if (tv1->v_type == VAR_OBJECT || tv2->v_type == VAR_OBJECT)
|
||||
{
|
||||
if (typval_compare_object(tv1, tv2, type, ic, &res) == FAIL)
|
||||
@@ -1649,6 +1738,47 @@ typval_compare_list(
|
||||
return OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* Compare "tv1" to "tv2" as tuples according to "type" and "ic".
|
||||
* Put the result, false or true, in "res".
|
||||
* Return FAIL and give an error message when the comparison can't be done.
|
||||
*/
|
||||
int
|
||||
typval_compare_tuple(
|
||||
typval_T *tv1,
|
||||
typval_T *tv2,
|
||||
exprtype_T type,
|
||||
int ic,
|
||||
int *res)
|
||||
{
|
||||
int val = 0;
|
||||
|
||||
if (type == EXPR_IS || type == EXPR_ISNOT)
|
||||
{
|
||||
val = (tv1->v_type == tv2->v_type
|
||||
&& tv1->vval.v_tuple == tv2->vval.v_tuple);
|
||||
if (type == EXPR_ISNOT)
|
||||
val = !val;
|
||||
}
|
||||
else if (tv1->v_type != tv2->v_type
|
||||
|| (type != EXPR_EQUAL && type != EXPR_NEQUAL))
|
||||
{
|
||||
if (tv1->v_type != tv2->v_type)
|
||||
emsg(_(e_can_only_compare_tuple_with_tuple));
|
||||
else
|
||||
emsg(_(e_invalid_operation_for_tuple));
|
||||
return FAIL;
|
||||
}
|
||||
else
|
||||
{
|
||||
val = tuple_equal(tv1->vval.v_tuple, tv2->vval.v_tuple, ic);
|
||||
if (type == EXPR_NEQUAL)
|
||||
val = !val;
|
||||
}
|
||||
*res = val;
|
||||
return OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* Compare v:null with another type. Return TRUE if the value is NULL.
|
||||
*/
|
||||
@@ -1674,6 +1804,7 @@ typval_compare_null(typval_T *tv1, typval_T *tv2)
|
||||
case VAR_JOB: return tv->vval.v_job == NULL;
|
||||
#endif
|
||||
case VAR_LIST: return tv->vval.v_list == NULL;
|
||||
case VAR_TUPLE: return tv->vval.v_tuple == NULL;
|
||||
case VAR_OBJECT: return tv->vval.v_object == NULL;
|
||||
case VAR_PARTIAL: return tv->vval.v_partial == NULL;
|
||||
case VAR_STRING: return tv->vval.v_string == NULL;
|
||||
@@ -2078,6 +2209,12 @@ tv_equal(
|
||||
--recursive_cnt;
|
||||
return r;
|
||||
|
||||
case VAR_TUPLE:
|
||||
++recursive_cnt;
|
||||
r = tuple_equal(tv1->vval.v_tuple, tv2->vval.v_tuple, ic);
|
||||
--recursive_cnt;
|
||||
return r;
|
||||
|
||||
case VAR_DICT:
|
||||
++recursive_cnt;
|
||||
r = dict_equal(tv1->vval.v_dict, tv2->vval.v_dict, ic);
|
||||
|
||||
+7
-7
@@ -7286,9 +7286,9 @@ set_ref_in_previous_funccal(int copyID)
|
||||
for (fc = previous_funccal; fc != NULL; fc = fc->fc_caller)
|
||||
{
|
||||
fc->fc_copyID = copyID + 1;
|
||||
if (set_ref_in_ht(&fc->fc_l_vars.dv_hashtab, copyID + 1, NULL)
|
||||
|| set_ref_in_ht(&fc->fc_l_avars.dv_hashtab, copyID + 1, NULL)
|
||||
|| set_ref_in_list_items(&fc->fc_l_varlist, copyID + 1, NULL))
|
||||
if (set_ref_in_ht(&fc->fc_l_vars.dv_hashtab, copyID + 1, NULL, NULL)
|
||||
|| set_ref_in_ht(&fc->fc_l_avars.dv_hashtab, copyID + 1, NULL, NULL)
|
||||
|| set_ref_in_list_items(&fc->fc_l_varlist, copyID + 1, NULL, NULL))
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
@@ -7300,9 +7300,9 @@ set_ref_in_funccal(funccall_T *fc, int copyID)
|
||||
if (fc->fc_copyID != copyID)
|
||||
{
|
||||
fc->fc_copyID = copyID;
|
||||
if (set_ref_in_ht(&fc->fc_l_vars.dv_hashtab, copyID, NULL)
|
||||
|| set_ref_in_ht(&fc->fc_l_avars.dv_hashtab, copyID, NULL)
|
||||
|| set_ref_in_list_items(&fc->fc_l_varlist, copyID, NULL)
|
||||
if (set_ref_in_ht(&fc->fc_l_vars.dv_hashtab, copyID, NULL, NULL)
|
||||
|| set_ref_in_ht(&fc->fc_l_avars.dv_hashtab, copyID, NULL, NULL)
|
||||
|| set_ref_in_list_items(&fc->fc_l_varlist, copyID, NULL, NULL)
|
||||
|| set_ref_in_func(NULL, fc->fc_func, copyID))
|
||||
return TRUE;
|
||||
}
|
||||
@@ -7365,7 +7365,7 @@ set_ref_in_func_args(int copyID)
|
||||
|
||||
for (i = 0; i < funcargs.ga_len; ++i)
|
||||
if (set_ref_in_item(((typval_T **)funcargs.ga_data)[i],
|
||||
copyID, NULL, NULL))
|
||||
copyID, NULL, NULL, NULL))
|
||||
return TRUE;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
@@ -704,6 +704,8 @@ static char *(features[]) =
|
||||
|
||||
static int included_patches[] =
|
||||
{ /* Add new patch number below this line */
|
||||
/**/
|
||||
1232,
|
||||
/**/
|
||||
1231,
|
||||
/**/
|
||||
|
||||
@@ -2201,7 +2201,8 @@ typedef int sock_T;
|
||||
#define VV_TYPE_ENUM 108
|
||||
#define VV_TYPE_ENUMVALUE 109
|
||||
#define VV_STACKTRACE 110
|
||||
#define VV_LEN 111 // number of v: vars
|
||||
#define VV_TYPE_TUPLE 111
|
||||
#define VV_LEN 112 // number of v: vars
|
||||
|
||||
// used for v_number in VAR_BOOL and VAR_SPECIAL
|
||||
#define VVAL_FALSE 0L // VAR_BOOL
|
||||
@@ -2227,6 +2228,7 @@ typedef int sock_T;
|
||||
#define VAR_TYPE_TYPEALIAS 14
|
||||
#define VAR_TYPE_ENUM 15
|
||||
#define VAR_TYPE_ENUMVALUE 16
|
||||
#define VAR_TYPE_TUPLE 17
|
||||
|
||||
#define DICT_MAXNEST 100 // maximum nesting of lists and dicts
|
||||
|
||||
|
||||
@@ -105,6 +105,8 @@ typedef enum {
|
||||
ISN_PUSHCLASS, // push class, uses isn_arg.classarg
|
||||
ISN_NEWLIST, // push list from stack items, size is isn_arg.number
|
||||
// -1 for null_list
|
||||
ISN_NEWTUPLE, // push tuple from stack items, size is isn_arg.number
|
||||
// -1 for null_list
|
||||
ISN_NEWDICT, // push dict from stack items, size is isn_arg.number
|
||||
// -1 for null_dict
|
||||
ISN_NEWPARTIAL, // push NULL partial
|
||||
@@ -149,6 +151,7 @@ typedef enum {
|
||||
|
||||
// more expression operations
|
||||
ISN_ADDLIST, // add two lists
|
||||
ISN_ADDTUPLE, // add two tuples
|
||||
ISN_ADDBLOB, // add two blobs
|
||||
|
||||
// operation with two arguments; isn_arg.op.op_type is exprtype_T
|
||||
@@ -165,6 +168,7 @@ typedef enum {
|
||||
ISN_COMPARESTRING,
|
||||
ISN_COMPAREBLOB,
|
||||
ISN_COMPARELIST,
|
||||
ISN_COMPARETUPLE,
|
||||
ISN_COMPAREDICT,
|
||||
ISN_COMPAREFUNC,
|
||||
ISN_COMPAREANY,
|
||||
@@ -177,6 +181,8 @@ typedef enum {
|
||||
ISN_LISTAPPEND, // append to a list, like add()
|
||||
ISN_LISTINDEX, // [expr] list index
|
||||
ISN_LISTSLICE, // [expr:expr] list slice
|
||||
ISN_TUPLEINDEX, // [expr] tuple index
|
||||
ISN_TUPLESLICE, // [expr:expr] tuple slice
|
||||
ISN_BLOBINDEX, // [expr] blob index
|
||||
ISN_BLOBSLICE, // [expr:expr] blob slice
|
||||
ISN_ANYINDEX, // [expr] runtime index
|
||||
|
||||
+1
-1
@@ -3650,7 +3650,7 @@ class_free_nonref(int copyID)
|
||||
set_ref_in_classes(int copyID)
|
||||
{
|
||||
for (class_T *cl = first_class; cl != NULL; cl = cl->class_next_used)
|
||||
set_ref_in_item_class(cl, copyID, NULL, NULL);
|
||||
set_ref_in_item_class(cl, copyID, NULL, NULL, NULL);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
@@ -870,6 +870,40 @@ compile_fill_loop_info(loop_info_T *loop_info, int funcref_idx, cctx_T *cctx)
|
||||
loop_info->li_closure_count = cctx->ctx_closure_count;
|
||||
}
|
||||
|
||||
/*
|
||||
* When compiling a for loop to iterate over a tuple, get the type of the loop
|
||||
* variable to use.
|
||||
*/
|
||||
static type_T *
|
||||
compile_for_tuple_get_vartype(type_T *vartype, int var_list)
|
||||
{
|
||||
// If this is not a variadic tuple, or all the tuple items don't have
|
||||
// the same type, then use t_any
|
||||
if (!(vartype->tt_flags & TTFLAG_VARARGS) || vartype->tt_argcount != 1)
|
||||
return &t_any;
|
||||
|
||||
// variadic tuple
|
||||
type_T *member_type = vartype->tt_args[0]->tt_member;
|
||||
if (member_type->tt_type == VAR_ANY)
|
||||
return &t_any;
|
||||
|
||||
if (!var_list)
|
||||
// for x in tuple<...list<xxx>>
|
||||
return member_type;
|
||||
|
||||
if (member_type->tt_type == VAR_LIST
|
||||
&& member_type->tt_member->tt_type != VAR_ANY)
|
||||
// for [x, y] in tuple<...list<list<xxx>>>
|
||||
return member_type->tt_member;
|
||||
else if (member_type->tt_type == VAR_TUPLE
|
||||
&& member_type->tt_flags & TTFLAG_VARARGS
|
||||
&& member_type->tt_argcount == 1)
|
||||
// for [x, y] in tuple<...list<tuple<...list<xxx>>>>
|
||||
return member_type->tt_args[0]->tt_member;
|
||||
|
||||
return &t_any;
|
||||
}
|
||||
|
||||
/*
|
||||
* Compile "for var in expr":
|
||||
*
|
||||
@@ -1000,6 +1034,7 @@ compile_for(char_u *arg_start, cctx_T *cctx)
|
||||
// give an error now.
|
||||
vartype = get_type_on_stack(cctx, 0);
|
||||
if (vartype->tt_type != VAR_LIST
|
||||
&& vartype->tt_type != VAR_TUPLE
|
||||
&& vartype->tt_type != VAR_STRING
|
||||
&& vartype->tt_type != VAR_BLOB
|
||||
&& vartype->tt_type != VAR_ANY
|
||||
@@ -1024,6 +1059,8 @@ compile_for(char_u *arg_start, cctx_T *cctx)
|
||||
&& vartype->tt_member->tt_member->tt_type != VAR_ANY)
|
||||
item_type = vartype->tt_member->tt_member;
|
||||
}
|
||||
else if (vartype->tt_type == VAR_TUPLE)
|
||||
item_type = compile_for_tuple_get_vartype(vartype, var_list);
|
||||
|
||||
// CMDMOD_REV must come before the FOR instruction.
|
||||
generate_undo_cmdmods(cctx);
|
||||
|
||||
+70
-19
@@ -524,8 +524,10 @@ use_typecheck(type_T *actual, type_T *expected)
|
||||
return TRUE;
|
||||
if (actual->tt_type == VAR_OBJECT && expected->tt_type == VAR_OBJECT)
|
||||
return TRUE;
|
||||
if ((actual->tt_type == VAR_LIST || actual->tt_type == VAR_DICT)
|
||||
&& actual->tt_type == expected->tt_type)
|
||||
if ((actual->tt_type == VAR_LIST
|
||||
|| actual->tt_type == VAR_TUPLE
|
||||
|| actual->tt_type == VAR_DICT)
|
||||
&& actual->tt_type == expected->tt_type)
|
||||
// This takes care of a nested list or dict.
|
||||
return use_typecheck(actual->tt_member, expected->tt_member);
|
||||
return FALSE;
|
||||
@@ -2642,7 +2644,10 @@ compile_assign_unlet(
|
||||
&& lhs->lhs_type != &t_blob
|
||||
&& lhs->lhs_type != &t_any)
|
||||
{
|
||||
semsg(_(e_cannot_use_range_with_assignment_str), var_start);
|
||||
if (lhs->lhs_type->tt_type == VAR_TUPLE)
|
||||
emsg(_(e_cannot_slice_tuple));
|
||||
else
|
||||
semsg(_(e_cannot_use_range_with_assignment_str), var_start);
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
@@ -2743,7 +2748,10 @@ compile_assign_unlet(
|
||||
}
|
||||
else
|
||||
{
|
||||
emsg(_(e_indexable_type_required));
|
||||
if (dest_type == VAR_TUPLE)
|
||||
emsg(_(e_tuple_is_immutable));
|
||||
else
|
||||
emsg(_(e_indexable_type_required));
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
@@ -2785,6 +2793,9 @@ push_default_value(
|
||||
case VAR_LIST:
|
||||
r = generate_NEWLIST(cctx, 0, FALSE);
|
||||
break;
|
||||
case VAR_TUPLE:
|
||||
r = generate_NEWTUPLE(cctx, 0, FALSE);
|
||||
break;
|
||||
case VAR_DICT:
|
||||
r = generate_NEWDICT(cctx, 0, FALSE);
|
||||
break;
|
||||
@@ -3015,11 +3026,31 @@ compile_assign_list_check_rhs_type(cctx_T *cctx, cac_T *cac)
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
if (need_type(stacktype, &t_list_any, FALSE, -1, 0, cctx,
|
||||
FALSE, FALSE) == FAIL)
|
||||
if (stacktype->tt_type != VAR_LIST && stacktype->tt_type != VAR_TUPLE
|
||||
&& stacktype->tt_type != VAR_ANY)
|
||||
{
|
||||
emsg(_(e_list_or_tuple_required));
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
if (need_type(stacktype,
|
||||
stacktype->tt_type == VAR_TUPLE ? &t_tuple_any : &t_list_any,
|
||||
FALSE, -1, 0, cctx, FALSE, FALSE) == FAIL)
|
||||
return FAIL;
|
||||
|
||||
if (stacktype->tt_member != NULL)
|
||||
if (stacktype->tt_type == VAR_TUPLE)
|
||||
{
|
||||
if (stacktype->tt_argcount != 1)
|
||||
cac->cac_rhs_type = &t_any;
|
||||
else
|
||||
{
|
||||
if (stacktype->tt_flags & TTFLAG_VARARGS)
|
||||
cac->cac_rhs_type = stacktype->tt_args[0]->tt_member;
|
||||
else
|
||||
cac->cac_rhs_type = stacktype->tt_args[0];
|
||||
}
|
||||
}
|
||||
else if (stacktype->tt_member != NULL)
|
||||
cac->cac_rhs_type = stacktype->tt_member;
|
||||
|
||||
return OK;
|
||||
@@ -3043,7 +3074,7 @@ compile_assign_list_check_length(cctx_T *cctx, cac_T *cac)
|
||||
isn_T *isn = ((isn_T *)cac->cac_instr->ga_data) +
|
||||
cac->cac_instr->ga_len - 1;
|
||||
|
||||
if (isn->isn_type == ISN_NEWLIST)
|
||||
if (isn->isn_type == ISN_NEWLIST || isn->isn_type == ISN_NEWTUPLE)
|
||||
{
|
||||
did_check = TRUE;
|
||||
if (cac->cac_semicolon ?
|
||||
@@ -3408,6 +3439,17 @@ compile_assign_compound_op(cctx_T *cctx, cac_T *cac)
|
||||
type_T *expected;
|
||||
type_T *stacktype = NULL;
|
||||
|
||||
if (cac->cac_lhs.lhs_type->tt_type == VAR_TUPLE)
|
||||
{
|
||||
// compound operators are not supported with a tuple
|
||||
char_u op[2];
|
||||
|
||||
op[0] = *cac->cac_op;
|
||||
op[1] = NUL;
|
||||
semsg(_(e_wrong_variable_type_for_str_equal), op);
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
if (*cac->cac_op == '.')
|
||||
{
|
||||
if (may_generate_2STRING(-1, TOSTRING_NONE, cctx) == FAIL)
|
||||
@@ -3486,8 +3528,11 @@ compile_assign_generate_store(cctx_T *cctx, cac_T *cac)
|
||||
&& lhs->lhs_type->tt_member != NULL
|
||||
&& lhs->lhs_type->tt_member != &t_any
|
||||
&& lhs->lhs_type->tt_member != &t_unknown)
|
||||
// Set the type in the list or dict, so that it can be checked,
|
||||
// also in legacy script.
|
||||
// Set the type in the list or dict, so that it can be
|
||||
// checked, also in legacy script.
|
||||
generate_SETTYPE(cctx, lhs->lhs_type);
|
||||
else if (lhs->lhs_type->tt_type == VAR_TUPLE
|
||||
&& lhs->lhs_type->tt_argcount != 0)
|
||||
generate_SETTYPE(cctx, lhs->lhs_type);
|
||||
else if (inferred_type != NULL
|
||||
&& (inferred_type->tt_type == VAR_DICT
|
||||
@@ -3495,8 +3540,12 @@ compile_assign_generate_store(cctx_T *cctx, cac_T *cac)
|
||||
&& inferred_type->tt_member != NULL
|
||||
&& inferred_type->tt_member != &t_unknown
|
||||
&& inferred_type->tt_member != &t_any)
|
||||
// Set the type in the list or dict, so that it can be checked,
|
||||
// also in legacy script.
|
||||
// Set the type in the list or dict, so that it can be
|
||||
// checked, also in legacy script.
|
||||
generate_SETTYPE(cctx, inferred_type);
|
||||
else if (inferred_type != NULL
|
||||
&& inferred_type->tt_type == VAR_TUPLE
|
||||
&& inferred_type->tt_argcount > 0)
|
||||
generate_SETTYPE(cctx, inferred_type);
|
||||
|
||||
if (!cac->cac_skip_store &&
|
||||
@@ -4030,13 +4079,15 @@ obj_constructor_prologue(ufunc_T *ufunc, cctx_T *cctx)
|
||||
else
|
||||
push_default_value(cctx, m->ocm_type->tt_type, FALSE, NULL);
|
||||
|
||||
if ((m->ocm_type->tt_type == VAR_DICT
|
||||
|| m->ocm_type->tt_type == VAR_LIST)
|
||||
&& m->ocm_type->tt_member != NULL
|
||||
&& m->ocm_type->tt_member != &t_any
|
||||
&& m->ocm_type->tt_member != &t_unknown)
|
||||
// Set the type in the list or dict, so that it can be checked,
|
||||
// also in legacy script.
|
||||
if (((m->ocm_type->tt_type == VAR_DICT
|
||||
|| m->ocm_type->tt_type == VAR_LIST)
|
||||
&& m->ocm_type->tt_member != NULL
|
||||
&& m->ocm_type->tt_member != &t_any
|
||||
&& m->ocm_type->tt_member != &t_unknown)
|
||||
|| (m->ocm_type->tt_type == VAR_TUPLE
|
||||
&& m->ocm_type->tt_argcount > 0))
|
||||
// Set the type in the list, tuple or dict, so that it can be
|
||||
// checked, also in legacy script.
|
||||
generate_SETTYPE(cctx, m->ocm_type);
|
||||
|
||||
generate_STORE_THIS(cctx, i);
|
||||
|
||||
+344
-91
@@ -110,6 +110,11 @@ static garray_T profile_info_ga = {0, 0, sizeof(profinfo_T), 20, NULL};
|
||||
// Get pointer to a local variable on the stack. Negative for arguments.
|
||||
#define STACK_TV_VAR(idx) (((typval_T *)ectx->ec_stack.ga_data) + ectx->ec_frame_idx + STACK_FRAME_SIZE + idx)
|
||||
|
||||
// Return value for functions used to execute instructions
|
||||
#define EXEC_FAIL 0
|
||||
#define EXEC_OK 1
|
||||
#define EXEC_DONE 2
|
||||
|
||||
void
|
||||
to_string_error(vartype_T vartype)
|
||||
{
|
||||
@@ -206,6 +211,46 @@ exe_newlist(int count, ectx_T *ectx)
|
||||
return OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* Create a new tuple from "count" items at the bottom of the stack.
|
||||
* When "count" is zero an empty tuple is added to the stack.
|
||||
* When "count" is -1 a NULL tuple is added to the stack.
|
||||
*/
|
||||
static int
|
||||
exe_newtuple(int count, ectx_T *ectx)
|
||||
{
|
||||
tuple_T *tuple = NULL;
|
||||
int idx;
|
||||
typval_T *tv;
|
||||
|
||||
if (count >= 0)
|
||||
{
|
||||
tuple = tuple_alloc_with_items(count);
|
||||
if (tuple == NULL)
|
||||
return FAIL;
|
||||
for (idx = 0; idx < count; ++idx)
|
||||
tuple_set_item(tuple, idx, STACK_TV_BOT(idx - count));
|
||||
}
|
||||
|
||||
if (count > 0)
|
||||
ectx->ec_stack.ga_len -= count - 1;
|
||||
else if (GA_GROW_FAILS(&ectx->ec_stack, 1))
|
||||
{
|
||||
tuple_unref(tuple);
|
||||
return FAIL;
|
||||
}
|
||||
else
|
||||
++ectx->ec_stack.ga_len;
|
||||
tv = STACK_TV_BOT(-1);
|
||||
tv->v_type = VAR_TUPLE;
|
||||
tv->vval.v_tuple = tuple;
|
||||
tv->v_lock = 0;
|
||||
if (tuple != NULL)
|
||||
++tuple->tv_refcount;
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* Implementation of ISN_NEWDICT.
|
||||
* Returns FAIL on total failure, MAYBE on error.
|
||||
@@ -923,7 +968,7 @@ set_ref_in_funcstacks(int copyID)
|
||||
int i;
|
||||
|
||||
for (i = 0; i < funcstack->fs_ga.ga_len; ++i)
|
||||
if (set_ref_in_item(stack + i, copyID, NULL, NULL))
|
||||
if (set_ref_in_item(stack + i, copyID, NULL, NULL, NULL))
|
||||
return TRUE; // abort
|
||||
}
|
||||
return FALSE;
|
||||
@@ -1718,6 +1763,10 @@ allocate_if_null(svar_T *sv)
|
||||
if (tv->vval.v_list == NULL && sv->sv_type != &t_list_empty)
|
||||
(void)rettv_list_alloc(tv);
|
||||
break;
|
||||
case VAR_TUPLE:
|
||||
if (tv->vval.v_tuple == NULL && sv->sv_type != &t_tuple_empty)
|
||||
(void)rettv_tuple_alloc(tv);
|
||||
break;
|
||||
case VAR_DICT:
|
||||
if (tv->vval.v_dict == NULL && sv->sv_type != &t_dict_empty)
|
||||
(void)rettv_dict_alloc(tv);
|
||||
@@ -2464,6 +2513,11 @@ execute_storeindex(isn_T *iptr, ectx_T *ectx)
|
||||
clear_tv(&otv[lidx]);
|
||||
otv[lidx] = *tv;
|
||||
}
|
||||
else if (dest_type == VAR_TUPLE)
|
||||
{
|
||||
emsg(_(e_tuple_is_immutable));
|
||||
status = FAIL;
|
||||
}
|
||||
else
|
||||
{
|
||||
status = FAIL;
|
||||
@@ -2802,6 +2856,20 @@ execute_for(isn_T *iptr, ectx_T *ectx)
|
||||
++ectx->ec_stack.ga_len;
|
||||
}
|
||||
}
|
||||
else if (ltv->v_type == VAR_TUPLE)
|
||||
{
|
||||
tuple_T *tuple = ltv->vval.v_tuple;
|
||||
|
||||
// push the next item from the tuple
|
||||
++idxtv->vval.v_number;
|
||||
if (tuple == NULL || idxtv->vval.v_number >= TUPLE_LEN(tuple))
|
||||
jump = TRUE;
|
||||
else
|
||||
{
|
||||
copy_tv(TUPLE_ITEM(tuple, idxtv->vval.v_number), STACK_TV_BOT(0));
|
||||
++ectx->ec_stack.ga_len;
|
||||
}
|
||||
}
|
||||
else if (ltv->v_type == VAR_STRING)
|
||||
{
|
||||
char_u *str = ltv->vval.v_string;
|
||||
@@ -3066,7 +3134,7 @@ set_ref_in_loopvars(int copyID)
|
||||
int i;
|
||||
|
||||
for (i = 0; i < loopvars->lvs_ga.ga_len; ++i)
|
||||
if (set_ref_in_item(stack + i, copyID, NULL, NULL))
|
||||
if (set_ref_in_item(stack + i, copyID, NULL, NULL, NULL))
|
||||
return TRUE; // abort
|
||||
}
|
||||
return FALSE;
|
||||
@@ -3304,6 +3372,145 @@ isn_put_do(ectx_T *ectx, isn_T *iptr, typval_T *tv, int fixindent)
|
||||
vim_free(expr);
|
||||
}
|
||||
|
||||
/*
|
||||
* Execute the ISN_UNPACK instruction for a List
|
||||
*/
|
||||
static int
|
||||
exec_unpack_list(ectx_T *ectx, isn_T *iptr, typval_T *tv)
|
||||
{
|
||||
int count = iptr->isn_arg.unpack.unp_count;
|
||||
int semicolon = iptr->isn_arg.unpack.unp_semicolon;
|
||||
list_T *l;
|
||||
listitem_T *li;
|
||||
int i;
|
||||
|
||||
l = tv->vval.v_list;
|
||||
if (l == NULL
|
||||
|| l->lv_len < (semicolon ? count - 1 : count))
|
||||
{
|
||||
SOURCING_LNUM = iptr->isn_lnum;
|
||||
emsg(_(e_list_value_does_not_have_enough_items));
|
||||
return EXEC_FAIL;
|
||||
}
|
||||
else if (!semicolon && l->lv_len > count)
|
||||
{
|
||||
SOURCING_LNUM = iptr->isn_lnum;
|
||||
emsg(_(e_list_value_has_more_items_than_targets));
|
||||
return EXEC_FAIL;
|
||||
}
|
||||
|
||||
CHECK_LIST_MATERIALIZE(l);
|
||||
if (GA_GROW_FAILS(&ectx->ec_stack, count - 1))
|
||||
return EXEC_DONE;
|
||||
ectx->ec_stack.ga_len += count - 1;
|
||||
|
||||
// Variable after semicolon gets a list with the remaining
|
||||
// items.
|
||||
if (semicolon)
|
||||
{
|
||||
list_T *rem_list =
|
||||
list_alloc_with_items(l->lv_len - count + 1);
|
||||
|
||||
if (rem_list == NULL)
|
||||
return EXEC_DONE;
|
||||
tv = STACK_TV_BOT(-count);
|
||||
tv->vval.v_list = rem_list;
|
||||
++rem_list->lv_refcount;
|
||||
tv->v_lock = 0;
|
||||
li = l->lv_first;
|
||||
for (i = 0; i < count - 1; ++i)
|
||||
li = li->li_next;
|
||||
for (i = 0; li != NULL; ++i)
|
||||
{
|
||||
typval_T tvcopy;
|
||||
|
||||
copy_tv(&li->li_tv, &tvcopy);
|
||||
list_set_item(rem_list, i, &tvcopy);
|
||||
li = li->li_next;
|
||||
}
|
||||
--count;
|
||||
}
|
||||
|
||||
// Produce the values in reverse order, first item last.
|
||||
li = l->lv_first;
|
||||
for (i = 0; i < count; ++i)
|
||||
{
|
||||
tv = STACK_TV_BOT(-i - 1);
|
||||
copy_tv(&li->li_tv, tv);
|
||||
li = li->li_next;
|
||||
}
|
||||
|
||||
list_unref(l);
|
||||
|
||||
return EXEC_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* Execute the ISN_UNPACK instruction for a Tuple
|
||||
*/
|
||||
static int
|
||||
exec_unpack_tuple(ectx_T *ectx, isn_T *iptr, typval_T *tv)
|
||||
{
|
||||
int count = iptr->isn_arg.unpack.unp_count;
|
||||
int semicolon = iptr->isn_arg.unpack.unp_semicolon;
|
||||
tuple_T *tuple;
|
||||
int i;
|
||||
|
||||
tuple = tv->vval.v_tuple;
|
||||
if (tuple == NULL
|
||||
|| TUPLE_LEN(tuple) < (semicolon ? count - 1 : count))
|
||||
{
|
||||
SOURCING_LNUM = iptr->isn_lnum;
|
||||
emsg(_(e_more_targets_than_tuple_items));
|
||||
return EXEC_FAIL;
|
||||
}
|
||||
else if (!semicolon && TUPLE_LEN(tuple) > count)
|
||||
{
|
||||
SOURCING_LNUM = iptr->isn_lnum;
|
||||
emsg(_(e_less_targets_than_tuple_items));
|
||||
return EXEC_FAIL;
|
||||
}
|
||||
|
||||
if (GA_GROW_FAILS(&ectx->ec_stack, count - 1))
|
||||
return EXEC_DONE;
|
||||
ectx->ec_stack.ga_len += count - 1;
|
||||
|
||||
// Variable after semicolon gets a list with the remaining
|
||||
// items.
|
||||
if (semicolon)
|
||||
{
|
||||
list_T *rem_list =
|
||||
list_alloc_with_items(TUPLE_LEN(tuple) - count + 1);
|
||||
|
||||
if (rem_list == NULL)
|
||||
return EXEC_DONE;
|
||||
tv = STACK_TV_BOT(-count);
|
||||
tv->v_type = VAR_LIST;
|
||||
tv->vval.v_list = rem_list;
|
||||
++rem_list->lv_refcount;
|
||||
tv->v_lock = 0;
|
||||
for (i = count - 1; i < TUPLE_LEN(tuple); ++i)
|
||||
{
|
||||
typval_T tvcopy;
|
||||
|
||||
copy_tv(TUPLE_ITEM(tuple, i), &tvcopy);
|
||||
list_set_item(rem_list, i - (count - 1), &tvcopy);
|
||||
}
|
||||
--count;
|
||||
}
|
||||
|
||||
// Produce the values in reverse order, first item last.
|
||||
for (i = 0; i < count; ++i)
|
||||
{
|
||||
tv = STACK_TV_BOT(-i - 1);
|
||||
copy_tv(TUPLE_ITEM(tuple, i), tv);
|
||||
}
|
||||
|
||||
tuple_unref(tuple);
|
||||
|
||||
return EXEC_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* Execute instructions in execution context "ectx".
|
||||
* Return OK or FAIL;
|
||||
@@ -4534,6 +4741,12 @@ exec_instructions(ectx_T *ectx)
|
||||
goto theend;
|
||||
break;
|
||||
|
||||
// create a tuple from items on the stack
|
||||
case ISN_NEWTUPLE:
|
||||
if (exe_newtuple(iptr->isn_arg.number, ectx) == FAIL)
|
||||
goto theend;
|
||||
break;
|
||||
|
||||
// create a dict from items on the stack
|
||||
case ISN_NEWDICT:
|
||||
{
|
||||
@@ -4938,9 +5151,9 @@ exec_instructions(ectx_T *ectx)
|
||||
size_t argidx = ufunc->uf_def_args.ga_len
|
||||
+ iptr->isn_arg.jumparg.jump_arg_off
|
||||
+ STACK_FRAME_SIZE;
|
||||
type_T *t = ufunc->uf_arg_types[argidx];
|
||||
type_T *tuple = ufunc->uf_arg_types[argidx];
|
||||
CLEAR_POINTER(tv);
|
||||
tv->v_type = t->tt_type;
|
||||
tv->v_type = tuple->tt_type;
|
||||
}
|
||||
|
||||
if (iptr->isn_type == ISN_JUMP_IF_ARG_SET ? arg_set : !arg_set)
|
||||
@@ -5299,6 +5512,7 @@ exec_instructions(ectx_T *ectx)
|
||||
break;
|
||||
|
||||
case ISN_COMPARELIST:
|
||||
case ISN_COMPARETUPLE:
|
||||
case ISN_COMPAREDICT:
|
||||
case ISN_COMPAREFUNC:
|
||||
case ISN_COMPARESTRING:
|
||||
@@ -5318,6 +5532,11 @@ exec_instructions(ectx_T *ectx)
|
||||
status = typval_compare_list(tv1, tv2,
|
||||
exprtype, ic, &res);
|
||||
}
|
||||
else if (iptr->isn_type == ISN_COMPARETUPLE)
|
||||
{
|
||||
status = typval_compare_tuple(tv1, tv2,
|
||||
exprtype, ic, &res);
|
||||
}
|
||||
else if (iptr->isn_type == ISN_COMPAREDICT)
|
||||
{
|
||||
status = typval_compare_dict(tv1, tv2,
|
||||
@@ -5370,6 +5589,7 @@ exec_instructions(ectx_T *ectx)
|
||||
break;
|
||||
|
||||
case ISN_ADDLIST:
|
||||
case ISN_ADDTUPLE:
|
||||
case ISN_ADDBLOB:
|
||||
{
|
||||
typval_T *tv1 = STACK_TV_BOT(-2);
|
||||
@@ -5385,6 +5605,8 @@ exec_instructions(ectx_T *ectx)
|
||||
else
|
||||
eval_addlist(tv1, tv2);
|
||||
}
|
||||
else if (iptr->isn_type == ISN_ADDTUPLE)
|
||||
eval_addtuple(tv1, tv2);
|
||||
else
|
||||
eval_addblob(tv1, tv2);
|
||||
clear_tv(tv2);
|
||||
@@ -5455,6 +5677,14 @@ exec_instructions(ectx_T *ectx)
|
||||
--ectx->ec_stack.ga_len;
|
||||
break;
|
||||
}
|
||||
else if (tv1->v_type == VAR_TUPLE
|
||||
&& tv2->v_type == VAR_TUPLE)
|
||||
{
|
||||
eval_addtuple(tv1, tv2);
|
||||
clear_tv(tv2);
|
||||
--ectx->ec_stack.ga_len;
|
||||
break;
|
||||
}
|
||||
else if (tv1->v_type == VAR_BLOB
|
||||
&& tv2->v_type == VAR_BLOB)
|
||||
{
|
||||
@@ -5574,20 +5804,25 @@ exec_instructions(ectx_T *ectx)
|
||||
|
||||
case ISN_LISTINDEX:
|
||||
case ISN_LISTSLICE:
|
||||
case ISN_TUPLEINDEX:
|
||||
case ISN_TUPLESLICE:
|
||||
case ISN_BLOBINDEX:
|
||||
case ISN_BLOBSLICE:
|
||||
{
|
||||
int is_slice = iptr->isn_type == ISN_LISTSLICE
|
||||
|| iptr->isn_type == ISN_BLOBSLICE;
|
||||
|| iptr->isn_type == ISN_TUPLESLICE
|
||||
|| iptr->isn_type == ISN_BLOBSLICE;
|
||||
int is_blob = iptr->isn_type == ISN_BLOBINDEX
|
||||
|| iptr->isn_type == ISN_BLOBSLICE;
|
||||
int is_tuple = iptr->isn_type == ISN_TUPLEINDEX
|
||||
|| iptr->isn_type == ISN_TUPLESLICE;
|
||||
varnumber_T n1, n2;
|
||||
typval_T *val_tv;
|
||||
|
||||
// list index: list is at stack-2, index at stack-1
|
||||
// list slice: list is at stack-3, indexes at stack-2 and
|
||||
// stack-1
|
||||
// Same for blob.
|
||||
// Same for tuple and blob.
|
||||
val_tv = is_slice ? STACK_TV_BOT(-3) : STACK_TV_BOT(-2);
|
||||
|
||||
tv = STACK_TV_BOT(-1);
|
||||
@@ -5610,6 +5845,12 @@ exec_instructions(ectx_T *ectx)
|
||||
n1, n2, FALSE, tv) == FAIL)
|
||||
goto on_error;
|
||||
}
|
||||
else if (is_tuple)
|
||||
{
|
||||
if (tuple_slice_or_index(val_tv->vval.v_tuple,
|
||||
is_slice, n1, n2, FALSE, tv, TRUE) == FAIL)
|
||||
goto on_error;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (list_slice_or_index(val_tv->vval.v_list, is_slice,
|
||||
@@ -5648,24 +5889,48 @@ exec_instructions(ectx_T *ectx)
|
||||
|
||||
case ISN_SLICE:
|
||||
{
|
||||
list_T *list;
|
||||
int count = iptr->isn_arg.number;
|
||||
|
||||
// type will have been checked to be a list
|
||||
tv = STACK_TV_BOT(-1);
|
||||
list = tv->vval.v_list;
|
||||
|
||||
// no error for short list, expect it to be checked earlier
|
||||
if (list != NULL && list->lv_len >= count)
|
||||
if (tv->v_type == VAR_LIST)
|
||||
{
|
||||
list_T *newlist = list_slice(list,
|
||||
count, list->lv_len - 1);
|
||||
list_T *list = tv->vval.v_list;
|
||||
|
||||
if (newlist != NULL)
|
||||
// no error for short list, expect it to be checked
|
||||
// earlier
|
||||
if (list != NULL && list->lv_len >= count)
|
||||
{
|
||||
list_unref(list);
|
||||
tv->vval.v_list = newlist;
|
||||
++newlist->lv_refcount;
|
||||
list_T *newlist = list_slice(list,
|
||||
count, list->lv_len - 1);
|
||||
|
||||
if (newlist != NULL)
|
||||
{
|
||||
list_unref(list);
|
||||
tv->vval.v_list = newlist;
|
||||
++newlist->lv_refcount;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
tuple_T *tuple = tv->vval.v_tuple;
|
||||
|
||||
// no error for short tuple, expect it to be checked
|
||||
// earlier
|
||||
if (tuple != NULL && TUPLE_LEN(tuple) >= count)
|
||||
{
|
||||
tuple_T *newtuple;
|
||||
|
||||
newtuple = tuple_slice(tuple, count,
|
||||
TUPLE_LEN(tuple) - 1);
|
||||
if (newtuple != NULL)
|
||||
{
|
||||
tuple_unref(tuple);
|
||||
tv->v_type = VAR_TUPLE;
|
||||
tv->vval.v_tuple = newtuple;
|
||||
++newtuple->tv_refcount;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5674,17 +5939,24 @@ exec_instructions(ectx_T *ectx)
|
||||
case ISN_GETITEM:
|
||||
{
|
||||
listitem_T *li;
|
||||
typval_T *item_tv;
|
||||
getitem_T *gi = &iptr->isn_arg.getitem;
|
||||
|
||||
// Get list item: list is at stack-1, push item.
|
||||
// List type and length is checked for when compiling.
|
||||
tv = STACK_TV_BOT(-1 - gi->gi_with_op);
|
||||
li = list_find(tv->vval.v_list, gi->gi_index);
|
||||
if (tv->v_type == VAR_LIST)
|
||||
{
|
||||
li = list_find(tv->vval.v_list, gi->gi_index);
|
||||
item_tv = &li->li_tv;
|
||||
}
|
||||
else
|
||||
item_tv = TUPLE_ITEM(tv->vval.v_tuple, gi->gi_index);
|
||||
|
||||
if (GA_GROW_FAILS(&ectx->ec_stack, 1))
|
||||
goto theend;
|
||||
++ectx->ec_stack.ga_len;
|
||||
copy_tv(&li->li_tv, STACK_TV_BOT(-1));
|
||||
copy_tv(item_tv, STACK_TV_BOT(-1));
|
||||
|
||||
// Useful when used in unpack assignment. Reset at
|
||||
// ISN_DROP.
|
||||
@@ -5920,17 +6192,40 @@ exec_instructions(ectx_T *ectx)
|
||||
{
|
||||
int min_len = iptr->isn_arg.checklen.cl_min_len;
|
||||
list_T *list = NULL;
|
||||
tuple_T *tuple = NULL;
|
||||
int len = 0;
|
||||
|
||||
tv = STACK_TV_BOT(-1);
|
||||
|
||||
int len_check_failed = FALSE;
|
||||
if (tv->v_type == VAR_LIST)
|
||||
list = tv->vval.v_list;
|
||||
if (list == NULL || list->lv_len < min_len
|
||||
{
|
||||
list = tv->vval.v_list;
|
||||
if (list == NULL || list->lv_len < min_len
|
||||
|| (list->lv_len > min_len
|
||||
&& !iptr->isn_arg.checklen.cl_more_OK))
|
||||
len_check_failed = TRUE;
|
||||
if (list != NULL)
|
||||
len = list->lv_len;
|
||||
}
|
||||
else if (tv->v_type == VAR_TUPLE)
|
||||
{
|
||||
tuple = tv->vval.v_tuple;
|
||||
if (tuple == NULL || TUPLE_LEN(tuple) < min_len
|
||||
|| (TUPLE_LEN(tuple) > min_len
|
||||
&& !iptr->isn_arg.checklen.cl_more_OK))
|
||||
len_check_failed = TRUE;
|
||||
if (tuple != NULL)
|
||||
len = TUPLE_LEN(tuple);
|
||||
}
|
||||
else
|
||||
len_check_failed = TRUE;
|
||||
|
||||
if (len_check_failed)
|
||||
{
|
||||
SOURCING_LNUM = iptr->isn_lnum;
|
||||
semsg(_(e_expected_nr_items_but_got_nr),
|
||||
min_len, list == NULL ? 0 : list->lv_len);
|
||||
min_len, len);
|
||||
goto on_error;
|
||||
}
|
||||
}
|
||||
@@ -6026,78 +6321,25 @@ exec_instructions(ectx_T *ectx)
|
||||
break;
|
||||
|
||||
case ISN_UNPACK:
|
||||
// Check there is a valid list to unpack.
|
||||
tv = STACK_TV_BOT(-1);
|
||||
if (tv->v_type != VAR_LIST && tv->v_type != VAR_TUPLE)
|
||||
{
|
||||
int count = iptr->isn_arg.unpack.unp_count;
|
||||
int semicolon = iptr->isn_arg.unpack.unp_semicolon;
|
||||
list_T *l;
|
||||
listitem_T *li;
|
||||
int i;
|
||||
SOURCING_LNUM = iptr->isn_lnum;
|
||||
emsg(_(e_for_argument_must_be_sequence_of_lists_or_tuples));
|
||||
goto on_error;
|
||||
}
|
||||
|
||||
// Check there is a valid list to unpack.
|
||||
tv = STACK_TV_BOT(-1);
|
||||
if (tv->v_type != VAR_LIST)
|
||||
{
|
||||
SOURCING_LNUM = iptr->isn_lnum;
|
||||
emsg(_(e_for_argument_must_be_sequence_of_lists));
|
||||
int rc;
|
||||
if (tv->v_type == VAR_LIST)
|
||||
rc = exec_unpack_list(ectx, iptr, tv);
|
||||
else
|
||||
rc = exec_unpack_tuple(ectx, iptr, tv);
|
||||
if (rc != EXEC_OK)
|
||||
{
|
||||
if (rc == EXEC_FAIL)
|
||||
goto on_error;
|
||||
}
|
||||
l = tv->vval.v_list;
|
||||
if (l == NULL
|
||||
|| l->lv_len < (semicolon ? count - 1 : count))
|
||||
{
|
||||
SOURCING_LNUM = iptr->isn_lnum;
|
||||
emsg(_(e_list_value_does_not_have_enough_items));
|
||||
goto on_error;
|
||||
}
|
||||
else if (!semicolon && l->lv_len > count)
|
||||
{
|
||||
SOURCING_LNUM = iptr->isn_lnum;
|
||||
emsg(_(e_list_value_has_more_items_than_targets));
|
||||
goto on_error;
|
||||
}
|
||||
|
||||
CHECK_LIST_MATERIALIZE(l);
|
||||
if (GA_GROW_FAILS(&ectx->ec_stack, count - 1))
|
||||
goto theend;
|
||||
ectx->ec_stack.ga_len += count - 1;
|
||||
|
||||
// Variable after semicolon gets a list with the remaining
|
||||
// items.
|
||||
if (semicolon)
|
||||
{
|
||||
list_T *rem_list =
|
||||
list_alloc_with_items(l->lv_len - count + 1);
|
||||
|
||||
if (rem_list == NULL)
|
||||
goto theend;
|
||||
tv = STACK_TV_BOT(-count);
|
||||
tv->vval.v_list = rem_list;
|
||||
++rem_list->lv_refcount;
|
||||
tv->v_lock = 0;
|
||||
li = l->lv_first;
|
||||
for (i = 0; i < count - 1; ++i)
|
||||
li = li->li_next;
|
||||
for (i = 0; li != NULL; ++i)
|
||||
{
|
||||
typval_T tvcopy;
|
||||
|
||||
copy_tv(&li->li_tv, &tvcopy);
|
||||
list_set_item(rem_list, i, &tvcopy);
|
||||
li = li->li_next;
|
||||
}
|
||||
--count;
|
||||
}
|
||||
|
||||
// Produce the values in reverse order, first item last.
|
||||
li = l->lv_first;
|
||||
for (i = 0; i < count; ++i)
|
||||
{
|
||||
tv = STACK_TV_BOT(-i - 1);
|
||||
copy_tv(&li->li_tv, tv);
|
||||
li = li->li_next;
|
||||
}
|
||||
|
||||
list_unref(l);
|
||||
goto theend;
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -7183,6 +7425,10 @@ list_instructions(char *pfx, isn_T *instr, int instr_count, ufunc_T *ufunc)
|
||||
smsg("%s%4d NEWLIST size %lld", pfx, current,
|
||||
(varnumber_T)(iptr->isn_arg.number));
|
||||
break;
|
||||
case ISN_NEWTUPLE:
|
||||
smsg("%s%4d NEWTUPLE size %lld", pfx, current,
|
||||
(varnumber_T)(iptr->isn_arg.number));
|
||||
break;
|
||||
case ISN_NEWDICT:
|
||||
smsg("%s%4d NEWDICT size %lld", pfx, current,
|
||||
(varnumber_T)(iptr->isn_arg.number));
|
||||
@@ -7474,6 +7720,7 @@ list_instructions(char *pfx, isn_T *instr, int instr_count, ufunc_T *ufunc)
|
||||
case ISN_COMPARESTRING:
|
||||
case ISN_COMPAREBLOB:
|
||||
case ISN_COMPARELIST:
|
||||
case ISN_COMPARETUPLE:
|
||||
case ISN_COMPAREDICT:
|
||||
case ISN_COMPAREFUNC:
|
||||
case ISN_COMPAREOBJECT:
|
||||
@@ -7512,6 +7759,7 @@ list_instructions(char *pfx, isn_T *instr, int instr_count, ufunc_T *ufunc)
|
||||
type = "COMPARESTRING"; break;
|
||||
case ISN_COMPAREBLOB: type = "COMPAREBLOB"; break;
|
||||
case ISN_COMPARELIST: type = "COMPARELIST"; break;
|
||||
case ISN_COMPARETUPLE: type = "COMPARETUPLE"; break;
|
||||
case ISN_COMPAREDICT: type = "COMPAREDICT"; break;
|
||||
case ISN_COMPAREFUNC: type = "COMPAREFUNC"; break;
|
||||
case ISN_COMPAREOBJECT:
|
||||
@@ -7525,6 +7773,7 @@ list_instructions(char *pfx, isn_T *instr, int instr_count, ufunc_T *ufunc)
|
||||
break;
|
||||
|
||||
case ISN_ADDLIST: smsg("%s%4d ADDLIST", pfx, current); break;
|
||||
case ISN_ADDTUPLE: smsg("%s%4d ADDTUPLE", pfx, current); break;
|
||||
case ISN_ADDBLOB: smsg("%s%4d ADDBLOB", pfx, current); break;
|
||||
|
||||
// expression operations
|
||||
@@ -7540,6 +7789,8 @@ list_instructions(char *pfx, isn_T *instr, int instr_count, ufunc_T *ufunc)
|
||||
case ISN_BLOBAPPEND: smsg("%s%4d BLOBAPPEND", pfx, current); break;
|
||||
case ISN_LISTINDEX: smsg("%s%4d LISTINDEX", pfx, current); break;
|
||||
case ISN_LISTSLICE: smsg("%s%4d LISTSLICE", pfx, current); break;
|
||||
case ISN_TUPLEINDEX: smsg("%s%4d TUPLEINDEX", pfx, current); break;
|
||||
case ISN_TUPLESLICE: smsg("%s%4d TUPLESLICE", pfx, current); break;
|
||||
case ISN_ANYINDEX: smsg("%s%4d ANYINDEX", pfx, current); break;
|
||||
case ISN_ANYSLICE: smsg("%s%4d ANYSLICE", pfx, current); break;
|
||||
case ISN_SLICE: smsg("%s%4d SLICE %lld",
|
||||
@@ -7817,6 +8068,8 @@ tv2bool(typval_T *tv)
|
||||
return tv->vval.v_string != NULL && *tv->vval.v_string != NUL;
|
||||
case VAR_LIST:
|
||||
return tv->vval.v_list != NULL && tv->vval.v_list->lv_len > 0;
|
||||
case VAR_TUPLE:
|
||||
return tuple_len(tv->vval.v_tuple) > 0;
|
||||
case VAR_DICT:
|
||||
return tv->vval.v_dict != NULL
|
||||
&& tv->vval.v_dict->dv_hashtab.ht_used > 0;
|
||||
|
||||
+167
-3
@@ -73,7 +73,74 @@ clear_ppconst(ppconst_T *ppconst)
|
||||
}
|
||||
|
||||
/*
|
||||
* Compile getting a member from a list/dict/string/blob. Stack has the
|
||||
* Compile getting a member from a tuple. Stack has the indexable value and
|
||||
* the index or the two indexes of a slice.
|
||||
*/
|
||||
static int
|
||||
compile_tuple_member(
|
||||
type2_T *typep,
|
||||
int is_slice,
|
||||
cctx_T *cctx)
|
||||
{
|
||||
if (is_slice)
|
||||
{
|
||||
if (generate_instr_drop(cctx, ISN_TUPLESLICE, 2) == FAIL)
|
||||
return FAIL;
|
||||
// a copy is made so the member type is no longer declared
|
||||
if (typep->type_decl->tt_type == VAR_TUPLE)
|
||||
typep->type_decl = &t_tuple_any;
|
||||
|
||||
// a copy is made, the composite is no longer "const"
|
||||
if (typep->type_curr->tt_flags & TTFLAG_CONST)
|
||||
{
|
||||
type_T *type = copy_type(typep->type_curr, cctx->ctx_type_list);
|
||||
|
||||
if (type != typep->type_curr) // did get a copy
|
||||
{
|
||||
type->tt_flags &= ~(TTFLAG_CONST | TTFLAG_STATIC);
|
||||
typep->type_curr = type;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (typep->type_curr->tt_type == VAR_TUPLE)
|
||||
{
|
||||
if (typep->type_curr->tt_argcount == 1)
|
||||
{
|
||||
if (typep->type_curr->tt_flags & TTFLAG_VARARGS)
|
||||
typep->type_curr
|
||||
= typep->type_curr->tt_args[0]->tt_member;
|
||||
else
|
||||
typep->type_curr = typep->type_curr->tt_args[0];
|
||||
}
|
||||
else
|
||||
typep->type_curr = &t_any;
|
||||
if (typep->type_decl->tt_type == VAR_TUPLE)
|
||||
{
|
||||
if (typep->type_decl->tt_argcount == 1)
|
||||
{
|
||||
if (typep->type_decl->tt_flags & TTFLAG_VARARGS)
|
||||
typep->type_decl
|
||||
= typep->type_decl->tt_args[0]->tt_member;
|
||||
else
|
||||
typep->type_decl = typep->type_decl->tt_args[0];
|
||||
}
|
||||
else
|
||||
typep->type_curr = &t_any;
|
||||
}
|
||||
else
|
||||
typep->type_decl = typep->type_curr;
|
||||
}
|
||||
if (generate_instr_drop(cctx, ISN_TUPLEINDEX, 1) == FAIL)
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* Compile getting a member from a list/tuple/dict/string/blob. Stack has the
|
||||
* indexable value and the index or the two indexes of a slice.
|
||||
* "keeping_dict" is used for dict[func](arg) to pass dict to func.
|
||||
*/
|
||||
@@ -85,7 +152,7 @@ compile_member(int is_slice, int *keeping_dict, cctx_T *cctx)
|
||||
vartype_T vartype;
|
||||
type_T *idxtype;
|
||||
|
||||
// We can index a list, dict and blob. If we don't know the type
|
||||
// We can index a list, tuple, dict and blob. If we don't know the type
|
||||
// we can use the index value type. If we still don't know use an "ANY"
|
||||
// instruction.
|
||||
// TODO: what about the decl type?
|
||||
@@ -97,7 +164,8 @@ compile_member(int is_slice, int *keeping_dict, cctx_T *cctx)
|
||||
|| typep->type_curr->tt_type == VAR_UNKNOWN)
|
||||
&& idxtype == &t_string)
|
||||
vartype = VAR_DICT;
|
||||
if (vartype == VAR_STRING || vartype == VAR_LIST || vartype == VAR_BLOB)
|
||||
if (vartype == VAR_STRING || vartype == VAR_LIST || vartype == VAR_BLOB
|
||||
|| vartype == VAR_TUPLE)
|
||||
{
|
||||
if (need_type(idxtype, &t_number, FALSE,
|
||||
-1, 0, cctx, FALSE, FALSE) == FAIL)
|
||||
@@ -174,6 +242,11 @@ compile_member(int is_slice, int *keeping_dict, cctx_T *cctx)
|
||||
return FAIL;
|
||||
}
|
||||
}
|
||||
else if (vartype == VAR_TUPLE)
|
||||
{
|
||||
if (compile_tuple_member(typep, is_slice, cctx) == FAIL)
|
||||
return FAIL;
|
||||
}
|
||||
else if (vartype == VAR_LIST || typep->type_curr->tt_type == VAR_ANY
|
||||
|| typep->type_curr->tt_type == VAR_UNKNOWN)
|
||||
{
|
||||
@@ -1465,6 +1538,82 @@ compile_list(char_u **arg, cctx_T *cctx, ppconst_T *ppconst)
|
||||
return generate_NEWLIST(cctx, count, FALSE);
|
||||
}
|
||||
|
||||
/*
|
||||
* parse a tuple: (expr, expr)
|
||||
* "*arg" points to the ','.
|
||||
* ppconst->pp_is_const is set if all the items are constants.
|
||||
*/
|
||||
static int
|
||||
compile_tuple(
|
||||
char_u **arg,
|
||||
cctx_T *cctx,
|
||||
ppconst_T *ppconst,
|
||||
int first_item_const)
|
||||
{
|
||||
char_u *p = *arg + 1;
|
||||
char_u *whitep = *arg + 1;
|
||||
int count = 0;
|
||||
int is_const;
|
||||
int is_all_const = TRUE; // reset when non-const encountered
|
||||
int must_end = FALSE;
|
||||
|
||||
if (**arg != ')')
|
||||
{
|
||||
if (*p != ')' && !IS_WHITE_OR_NUL(*p))
|
||||
{
|
||||
semsg(_(e_white_space_required_after_str_str), ",", p - 1);
|
||||
return FAIL;
|
||||
}
|
||||
count = 1; // the first tuple item is already processed
|
||||
is_all_const = first_item_const;
|
||||
for (;;)
|
||||
{
|
||||
if (may_get_next_line(whitep, &p, cctx) == FAIL)
|
||||
{
|
||||
semsg(_(e_missing_end_of_tuple_rsp_str), *arg);
|
||||
return FAIL;
|
||||
}
|
||||
if (*p == ',')
|
||||
{
|
||||
semsg(_(e_no_white_space_allowed_before_str_str), ",", p);
|
||||
return FAIL;
|
||||
}
|
||||
if (*p == ')')
|
||||
{
|
||||
++p;
|
||||
break;
|
||||
}
|
||||
if (must_end)
|
||||
{
|
||||
semsg(_(e_missing_comma_in_tuple_str), p);
|
||||
return FAIL;
|
||||
}
|
||||
if (compile_expr0_ext(&p, cctx, &is_const) == FAIL)
|
||||
return FAIL;
|
||||
if (!is_const)
|
||||
is_all_const = FALSE;
|
||||
++count;
|
||||
if (*p == ',')
|
||||
{
|
||||
++p;
|
||||
if (*p != ')' && !IS_WHITE_OR_NUL(*p))
|
||||
{
|
||||
semsg(_(e_white_space_required_after_str_str), ",", p - 1);
|
||||
return FAIL;
|
||||
}
|
||||
}
|
||||
else
|
||||
must_end = TRUE;
|
||||
whitep = p;
|
||||
p = skipwhite(p);
|
||||
}
|
||||
}
|
||||
*arg = p;
|
||||
|
||||
ppconst->pp_is_const = is_all_const;
|
||||
return generate_NEWTUPLE(cctx, count, FALSE);
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse a lambda: "(arg, arg) => expr"
|
||||
* "*arg" points to the '('.
|
||||
@@ -2168,6 +2317,11 @@ compile_parenthesis(char_u **arg, cctx_T *cctx, ppconst_T *ppconst)
|
||||
|
||||
if (may_get_next_line_error(p, arg, cctx) == FAIL)
|
||||
return FAIL;
|
||||
|
||||
if (**arg == ')')
|
||||
// empty tuple
|
||||
return compile_tuple(arg, cctx, ppconst, FALSE);
|
||||
|
||||
if (ppconst->pp_used <= PPSIZE - 10)
|
||||
{
|
||||
ret = compile_expr1(arg, cctx, ppconst);
|
||||
@@ -2181,6 +2335,15 @@ compile_parenthesis(char_u **arg, cctx_T *cctx, ppconst_T *ppconst)
|
||||
}
|
||||
if (may_get_next_line_error(*arg, arg, cctx) == FAIL)
|
||||
return FAIL;
|
||||
if (ret == OK && **arg == ',')
|
||||
{
|
||||
// tuple
|
||||
int is_const = ppconst->pp_used > 0 || ppconst->pp_is_const;
|
||||
if (generate_ppconst(cctx, ppconst) == FAIL)
|
||||
return FAIL;
|
||||
return compile_tuple(arg, cctx, ppconst, is_const);
|
||||
}
|
||||
|
||||
if (**arg == ')')
|
||||
++*arg;
|
||||
else if (ret == OK)
|
||||
@@ -2440,6 +2603,7 @@ compile_subscript(
|
||||
int is_slice = FALSE;
|
||||
|
||||
// list index: list[123]
|
||||
// tuple index: tuple[123]
|
||||
// dict member: dict[key]
|
||||
// string index: text[123]
|
||||
// blob index: blob[123]
|
||||
|
||||
+115
-2
@@ -224,6 +224,7 @@ may_generate_2STRING(int offset, int tostring_flags, cctx_T *cctx)
|
||||
|
||||
// conversion possible when tolerant
|
||||
case VAR_LIST:
|
||||
case VAR_TUPLE:
|
||||
case VAR_DICT:
|
||||
if (tostring_flags & TOSTRING_TOLERANT)
|
||||
{
|
||||
@@ -280,6 +281,58 @@ check_number_or_float(type_T *typ1, type_T *typ2, char_u *op)
|
||||
return OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* Append the tuple item types from "tuple_type" to the grow array "gap".
|
||||
*/
|
||||
static int
|
||||
ga_append_tuple_types(type_T *tuple_type, garray_T *gap)
|
||||
{
|
||||
for (int i = 0; i < tuple_type->tt_argcount; i++)
|
||||
{
|
||||
if (ga_grow(gap, 1) == FAIL)
|
||||
return FAIL;
|
||||
|
||||
((type_T **)gap->ga_data)[gap->ga_len] = tuple_type->tt_args[i];
|
||||
gap->ga_len++;
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* When concatenating two tuples, the resulting tuple gets a union of item
|
||||
* types from both the tuples. This function sets the union tuple type in the
|
||||
* stack.
|
||||
*
|
||||
* Returns OK on success and FAIL on memory allocation failure.
|
||||
*/
|
||||
static int
|
||||
set_tuple_union_type_on_stack(type_T *type1, type_T *type2, cctx_T *cctx)
|
||||
{
|
||||
// The concatenated tuple has the union of types from both the tuples
|
||||
garray_T tuple_types_ga;
|
||||
|
||||
ga_init2(&tuple_types_ga, sizeof(type_T *), 10);
|
||||
|
||||
if (type1->tt_argcount > 0)
|
||||
ga_append_tuple_types(type1, &tuple_types_ga);
|
||||
if (!(type1->tt_flags & TTFLAG_VARARGS) && (type2->tt_argcount > 0))
|
||||
ga_append_tuple_types(type2, &tuple_types_ga);
|
||||
|
||||
type_T *new_tuple_type = get_tuple_type(&tuple_types_ga,
|
||||
cctx->ctx_type_list);
|
||||
// result inherits the variadic flag from the operands
|
||||
new_tuple_type->tt_flags |= (type1->tt_flags & TTFLAG_VARARGS)
|
||||
| (type2->tt_flags & TTFLAG_VARARGS);
|
||||
|
||||
// set the type on the stack for the resulting tuple
|
||||
set_type_on_stack(cctx, new_tuple_type, 0);
|
||||
|
||||
ga_clear(&tuple_types_ga);
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* Generate instruction for "+". For a list this creates a new list.
|
||||
*/
|
||||
@@ -294,11 +347,12 @@ generate_add_instr(
|
||||
isn_T *isn = generate_instr_drop(cctx,
|
||||
vartype == VAR_NUMBER ? ISN_OPNR
|
||||
: vartype == VAR_LIST ? ISN_ADDLIST
|
||||
: vartype == VAR_TUPLE ? ISN_ADDTUPLE
|
||||
: vartype == VAR_BLOB ? ISN_ADDBLOB
|
||||
: vartype == VAR_FLOAT ? ISN_OPFLOAT
|
||||
: ISN_OPANY, 1);
|
||||
|
||||
if (vartype != VAR_LIST && vartype != VAR_BLOB
|
||||
if (vartype != VAR_LIST && vartype != VAR_BLOB && vartype != VAR_TUPLE
|
||||
&& type1->tt_type != VAR_ANY
|
||||
&& type1->tt_type != VAR_UNKNOWN
|
||||
&& type2->tt_type != VAR_ANY
|
||||
@@ -320,6 +374,14 @@ generate_add_instr(
|
||||
&& type1->tt_type == VAR_LIST && type2->tt_type == VAR_LIST
|
||||
&& type1->tt_member != type2->tt_member)
|
||||
set_type_on_stack(cctx, &t_list_any, 0);
|
||||
else if (vartype == VAR_TUPLE)
|
||||
{
|
||||
if (!check_tuples_addable(type1, type2))
|
||||
return FAIL;
|
||||
|
||||
if (set_tuple_union_type_on_stack(type1, type2, cctx) == FAIL)
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
return isn == NULL ? FAIL : OK;
|
||||
}
|
||||
@@ -335,6 +397,7 @@ 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
|
||||
|| type1->tt_type == VAR_TUPLE
|
||||
|| type1->tt_type == VAR_FLOAT
|
||||
|| type1->tt_type == VAR_BLOB))
|
||||
return type1->tt_type;
|
||||
@@ -461,6 +524,7 @@ get_compare_isn(
|
||||
case VAR_STRING: isntype = ISN_COMPARESTRING; break;
|
||||
case VAR_BLOB: isntype = ISN_COMPAREBLOB; break;
|
||||
case VAR_LIST: isntype = ISN_COMPARELIST; break;
|
||||
case VAR_TUPLE: isntype = ISN_COMPARETUPLE; break;
|
||||
case VAR_DICT: isntype = ISN_COMPAREDICT; break;
|
||||
case VAR_FUNC: isntype = ISN_COMPAREFUNC; break;
|
||||
case VAR_OBJECT: isntype = ISN_COMPAREOBJECT; break;
|
||||
@@ -744,6 +808,11 @@ generate_tv_PUSH(cctx_T *cctx, typval_T *tv)
|
||||
iemsg("non-empty list constant not supported");
|
||||
generate_NEWLIST(cctx, 0, TRUE);
|
||||
break;
|
||||
case VAR_TUPLE:
|
||||
if (tv->vval.v_tuple != NULL)
|
||||
iemsg("non-empty tuple constant not supported");
|
||||
generate_NEWTUPLE(cctx, 0, TRUE);
|
||||
break;
|
||||
case VAR_DICT:
|
||||
if (tv->vval.v_dict != NULL)
|
||||
iemsg("non-empty dict constant not supported");
|
||||
@@ -1009,7 +1078,7 @@ generate_GETITEM(cctx_T *cctx, int index, int with_op)
|
||||
|
||||
RETURN_OK_IF_SKIP(cctx);
|
||||
|
||||
item_type = type->tt_member;
|
||||
item_type = get_item_type(type);
|
||||
if ((isn = generate_instr(cctx, ISN_GETITEM)) == NULL)
|
||||
return FAIL;
|
||||
isn->isn_arg.getitem.gi_index = index;
|
||||
@@ -1369,6 +1438,45 @@ generate_NEWLIST(cctx_T *cctx, int count, int use_null)
|
||||
return push_type_stack2(cctx, type, decl_type);
|
||||
}
|
||||
|
||||
/*
|
||||
* Generate an ISN_NEWTUPLE instruction for "count" items.
|
||||
* "use_null" is TRUE for null_tuple.
|
||||
*/
|
||||
int
|
||||
generate_NEWTUPLE(cctx_T *cctx, int count, int use_null)
|
||||
{
|
||||
isn_T *isn;
|
||||
type_T *type;
|
||||
type_T *decl_type;
|
||||
|
||||
RETURN_OK_IF_SKIP(cctx);
|
||||
if ((isn = generate_instr(cctx, ISN_NEWTUPLE)) == NULL)
|
||||
return FAIL;
|
||||
isn->isn_arg.number = use_null ? -1 : count;
|
||||
|
||||
// Get the member type and the declared member type from all the items on
|
||||
// the stack.
|
||||
garray_T tuple_types_ga;
|
||||
ga_init2(&tuple_types_ga, sizeof(type_T *), 10);
|
||||
|
||||
if (get_tuple_type_from_stack(count, &tuple_types_ga, cctx) < 0)
|
||||
{
|
||||
ga_clear(&tuple_types_ga);
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
type = get_tuple_type(&tuple_types_ga, cctx->ctx_type_list);
|
||||
decl_type = &t_tuple_any;
|
||||
|
||||
ga_clear(&tuple_types_ga);
|
||||
|
||||
// drop the value types
|
||||
cctx->ctx_type_stack.ga_len -= count;
|
||||
|
||||
// add the tuple type to the type stack
|
||||
return push_type_stack2(cctx, type, decl_type);
|
||||
}
|
||||
|
||||
/*
|
||||
* Generate an ISN_NEWDICT instruction.
|
||||
* "use_null" is TRUE for null_dict.
|
||||
@@ -2738,6 +2846,7 @@ delete_instr(isn_T *isn)
|
||||
case ISN_2STRING_ANY:
|
||||
case ISN_ADDBLOB:
|
||||
case ISN_ADDLIST:
|
||||
case ISN_ADDTUPLE:
|
||||
case ISN_ANYINDEX:
|
||||
case ISN_ANYSLICE:
|
||||
case ISN_BCALL:
|
||||
@@ -2756,6 +2865,7 @@ delete_instr(isn_T *isn)
|
||||
case ISN_COMPAREFLOAT:
|
||||
case ISN_COMPAREFUNC:
|
||||
case ISN_COMPARELIST:
|
||||
case ISN_COMPARETUPLE:
|
||||
case ISN_COMPARENR:
|
||||
case ISN_COMPARENULL:
|
||||
case ISN_COMPAREOBJECT:
|
||||
@@ -2787,6 +2897,8 @@ delete_instr(isn_T *isn)
|
||||
case ISN_LISTAPPEND:
|
||||
case ISN_LISTINDEX:
|
||||
case ISN_LISTSLICE:
|
||||
case ISN_TUPLEINDEX:
|
||||
case ISN_TUPLESLICE:
|
||||
case ISN_LOAD:
|
||||
case ISN_LOADBDICT:
|
||||
case ISN_LOADGDICT:
|
||||
@@ -2800,6 +2912,7 @@ delete_instr(isn_T *isn)
|
||||
case ISN_NEGATENR:
|
||||
case ISN_NEWDICT:
|
||||
case ISN_NEWLIST:
|
||||
case ISN_NEWTUPLE:
|
||||
case ISN_NEWPARTIAL:
|
||||
case ISN_OPANY:
|
||||
case ISN_OPFLOAT:
|
||||
|
||||
@@ -1127,6 +1127,7 @@ static char *reserved[] = {
|
||||
"null_dict",
|
||||
"null_function",
|
||||
"null_list",
|
||||
"null_tuple",
|
||||
"null_partial",
|
||||
"null_string",
|
||||
"null_channel",
|
||||
|
||||
+505
-10
@@ -202,7 +202,7 @@ set_tv_type_recurse(type_T *type)
|
||||
{
|
||||
return type->tt_member != NULL
|
||||
&& (type->tt_member->tt_type == VAR_DICT
|
||||
|| type->tt_member->tt_type == VAR_LIST)
|
||||
|| type->tt_member->tt_type == VAR_LIST)
|
||||
&& type->tt_member->tt_member != NULL
|
||||
&& type->tt_member->tt_member != &t_any
|
||||
&& type->tt_member->tt_member != &t_unknown;
|
||||
@@ -262,7 +262,37 @@ set_tv_type_list(list_T *l, type_T *type)
|
||||
}
|
||||
|
||||
/*
|
||||
* Set the type of "tv" to "type" if it is a list or dict.
|
||||
* Set the type of Tuple "tuple" to "type"
|
||||
*/
|
||||
static void
|
||||
set_tv_type_tuple(tuple_T *tuple, type_T *type)
|
||||
{
|
||||
if (tuple->tv_type == type)
|
||||
return;
|
||||
|
||||
free_type(tuple->tv_type);
|
||||
tuple->tv_type = alloc_type(type);
|
||||
|
||||
if (type->tt_argcount <= 0)
|
||||
return;
|
||||
|
||||
// recursively set the type of list items
|
||||
type_T *item_type;
|
||||
for (int i = 0; i < tuple_len(tuple); i++)
|
||||
{
|
||||
if ((type->tt_flags & TTFLAG_VARARGS) && (i >= type->tt_argcount - 1))
|
||||
// For a variadic tuple, the last type is a List. So use the
|
||||
// List member type.
|
||||
item_type = type->tt_args[type->tt_argcount - 1]->tt_member;
|
||||
else
|
||||
item_type = type->tt_args[i];
|
||||
|
||||
set_tv_type(TUPLE_ITEM(tuple, i), item_type);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Set the type of "tv" to "type" if it is a list or tuple or dict.
|
||||
*/
|
||||
void
|
||||
set_tv_type(typval_T *tv, type_T *type)
|
||||
@@ -276,6 +306,31 @@ set_tv_type(typval_T *tv, type_T *type)
|
||||
set_tv_type_dict(tv->vval.v_dict, type);
|
||||
else if (tv->v_type == VAR_LIST && tv->vval.v_list != NULL)
|
||||
set_tv_type_list(tv->vval.v_list, type);
|
||||
else if (tv->v_type == VAR_TUPLE && tv->vval.v_tuple != NULL)
|
||||
set_tv_type_tuple(tv->vval.v_tuple, type);
|
||||
}
|
||||
|
||||
/*
|
||||
* For a tuple type, reserve space for "typecount" types (including the
|
||||
* repeated type).
|
||||
*/
|
||||
static int
|
||||
tuple_type_add_types(
|
||||
type_T *tupletype,
|
||||
int typecount,
|
||||
garray_T *type_gap)
|
||||
{
|
||||
// To make it easy to free the space needed for the types, add the
|
||||
// pointer to type_gap.
|
||||
if (ga_grow(type_gap, 1) == FAIL)
|
||||
return FAIL;
|
||||
tupletype->tt_args = ALLOC_CLEAR_MULT(type_T *, typecount);
|
||||
if (tupletype->tt_args == NULL)
|
||||
return FAIL;
|
||||
((type_T **)type_gap->ga_data)[type_gap->ga_len] =
|
||||
(void *)tupletype->tt_args;
|
||||
++type_gap->ga_len;
|
||||
return OK;
|
||||
}
|
||||
|
||||
type_T *
|
||||
@@ -307,6 +362,41 @@ get_list_type(type_T *member_type, garray_T *type_gap)
|
||||
return type;
|
||||
}
|
||||
|
||||
/*
|
||||
* Create and return a tuple type from the tuple item types in
|
||||
* "tuple_types_ga".
|
||||
*/
|
||||
type_T *
|
||||
get_tuple_type(
|
||||
garray_T *tuple_types_gap,
|
||||
garray_T *type_gap)
|
||||
{
|
||||
type_T *type;
|
||||
type_T **tuple_types = tuple_types_gap->ga_data;
|
||||
int typecount = tuple_types_gap->ga_len;
|
||||
|
||||
// recognize commonly used types
|
||||
if (typecount == 0)
|
||||
return &t_tuple_any;
|
||||
|
||||
// Not a common type, create a new entry.
|
||||
type = get_type_ptr(type_gap);
|
||||
if (type == NULL)
|
||||
return &t_any;
|
||||
type->tt_type = VAR_TUPLE;
|
||||
type->tt_member = NULL;
|
||||
if (typecount > 0)
|
||||
{
|
||||
if (tuple_type_add_types(type, typecount, type_gap) == FAIL)
|
||||
return NULL;
|
||||
mch_memmove(type->tt_args, tuple_types, sizeof(type_T *) * typecount);
|
||||
}
|
||||
type->tt_argcount = typecount;
|
||||
type->tt_flags = 0;
|
||||
|
||||
return type;
|
||||
}
|
||||
|
||||
type_T *
|
||||
get_dict_type(type_T *member_type, garray_T *type_gap)
|
||||
{
|
||||
@@ -353,6 +443,23 @@ alloc_func_type(type_T *ret_type, int argcount, garray_T *type_gap)
|
||||
return type;
|
||||
}
|
||||
|
||||
/*
|
||||
* Allocate a new type for a tuple.
|
||||
*/
|
||||
static type_T *
|
||||
alloc_tuple_type(int typecount, garray_T *type_gap)
|
||||
{
|
||||
type_T *type = get_type_ptr(type_gap);
|
||||
|
||||
if (type == NULL)
|
||||
return &t_any;
|
||||
type->tt_type = VAR_TUPLE;
|
||||
type->tt_member = NULL;
|
||||
type->tt_argcount = typecount;
|
||||
type->tt_args = NULL;
|
||||
return type;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get a function type, based on the return type "ret_type".
|
||||
* "argcount" must be -1 or 0, a predefined type can be used.
|
||||
@@ -506,6 +613,64 @@ list_typval2type(typval_T *tv, int copyID, garray_T *type_gap, int flags)
|
||||
return get_list_type(member_type, type_gap);
|
||||
}
|
||||
|
||||
/*
|
||||
* Get a type_T for a Tuple typval in "tv".
|
||||
* When "flags" has TVTT_DO_MEMBER also get the member type, otherwise use
|
||||
* "any".
|
||||
* When "flags" has TVTT_MORE_SPECIFIC get the more specific member type if it
|
||||
* is "any".
|
||||
*/
|
||||
static type_T *
|
||||
tuple_typval2type(typval_T *tv, int copyID, garray_T *type_gap, int flags)
|
||||
{
|
||||
tuple_T *tuple = tv->vval.v_tuple;
|
||||
int len = tuple_len(tuple);
|
||||
type_T *type = NULL;
|
||||
|
||||
// An empty tuple has type tuple<unknown>, unless the type was specified
|
||||
// and is not tuple<any>. This matters when assigning to a variable
|
||||
// with a specific tuple type.
|
||||
if (tuple == NULL || (len == 0 && (tuple->tv_type == NULL
|
||||
|| tuple->tv_type->tt_argcount == 0)))
|
||||
return &t_tuple_empty;
|
||||
|
||||
if ((flags & TVTT_DO_MEMBER) == 0)
|
||||
return &t_tuple_any;
|
||||
|
||||
// If the type is tuple<any> go through the members, it may end up a
|
||||
// more specific type.
|
||||
if (tuple->tv_type != NULL && (len == 0
|
||||
|| (flags & TVTT_MORE_SPECIFIC) == 0))
|
||||
// make a copy, tv_type may be freed if the tuple is freed
|
||||
return copy_type_deep(tuple->tv_type, type_gap);
|
||||
|
||||
if (tuple->tv_copyID == copyID)
|
||||
// avoid recursion
|
||||
return &t_tuple_any;
|
||||
|
||||
tuple->tv_copyID = copyID;
|
||||
|
||||
garray_T tuple_types_ga;
|
||||
ga_init2(&tuple_types_ga, sizeof(type_T *), 10);
|
||||
for (int i = 0; i < len; i++)
|
||||
{
|
||||
type = typval2type(TUPLE_ITEM(tuple, i), copyID, type_gap,
|
||||
TVTT_DO_MEMBER);
|
||||
if (ga_grow(&tuple_types_ga, 1) == FAIL)
|
||||
{
|
||||
ga_clear(&tuple_types_ga);
|
||||
return NULL;
|
||||
}
|
||||
((type_T **)tuple_types_ga.ga_data)[tuple_types_ga.ga_len] = type;
|
||||
tuple_types_ga.ga_len++;
|
||||
}
|
||||
|
||||
type_T *tuple_type = get_tuple_type(&tuple_types_ga, type_gap);
|
||||
ga_clear(&tuple_types_ga);
|
||||
|
||||
return tuple_type;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get a type_T for a Dict typval in "tv".
|
||||
* When "flags" has TVTT_DO_MEMBER also get the member type, otherwise use
|
||||
@@ -723,6 +888,9 @@ typval2type_int(typval_T *tv, int copyID, garray_T *type_gap, int flags)
|
||||
case VAR_LIST:
|
||||
return list_typval2type(tv, copyID, type_gap, flags);
|
||||
|
||||
case VAR_TUPLE:
|
||||
return tuple_typval2type(tv, copyID, type_gap, flags);
|
||||
|
||||
case VAR_DICT:
|
||||
return dict_typval2type(tv, copyID, type_gap, flags);
|
||||
|
||||
@@ -949,6 +1117,63 @@ type_mismatch_where(type_T *expected, type_T *actual, where_T where)
|
||||
vim_free(tofree2);
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if the expected and actual types match for a tuple
|
||||
*/
|
||||
static int
|
||||
check_tuple_type_maybe(
|
||||
type_T *expected,
|
||||
type_T *actual,
|
||||
where_T where)
|
||||
{
|
||||
if (expected->tt_argcount == -1 || actual->tt_argcount == -1
|
||||
|| expected->tt_args == NULL || actual->tt_args == NULL)
|
||||
return OK;
|
||||
|
||||
// For a non-variadic tuple, the number of items must match
|
||||
if (!(expected->tt_flags & TTFLAG_VARARGS)
|
||||
&& expected->tt_argcount != actual->tt_argcount)
|
||||
return FAIL;
|
||||
|
||||
// compare the type of each tuple item
|
||||
for (int i = 0; i < actual->tt_argcount; ++i)
|
||||
{
|
||||
type_T *exp_type;
|
||||
type_T *actual_type;
|
||||
|
||||
if (expected->tt_flags & TTFLAG_VARARGS)
|
||||
{
|
||||
if (i < expected->tt_argcount - 1)
|
||||
exp_type = expected->tt_args[i];
|
||||
else
|
||||
// For a variadic tuple, the last type is a List. So use the
|
||||
// List member type.
|
||||
exp_type = expected->tt_args[expected->tt_argcount - 1]->tt_member;
|
||||
}
|
||||
else
|
||||
exp_type = expected->tt_args[i];
|
||||
|
||||
if (actual->tt_flags & TTFLAG_VARARGS)
|
||||
{
|
||||
if (i < actual->tt_argcount - 1)
|
||||
actual_type = actual->tt_args[i];
|
||||
else
|
||||
// For a variadic tuple, the last type is a List. So use the
|
||||
// List member type.
|
||||
actual_type = actual->tt_args[actual->tt_argcount - 1]->tt_member;
|
||||
}
|
||||
else
|
||||
actual_type = actual->tt_args[i];
|
||||
|
||||
// Allow for using "any" type for a tuple item
|
||||
if (actual->tt_args[i] != &t_any && check_type(exp_type, actual_type,
|
||||
FALSE, where) == FAIL)
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if the expected and actual types match.
|
||||
* Does not allow for assigning "any" to a specific type.
|
||||
@@ -1018,6 +1243,8 @@ check_type_maybe(
|
||||
ret = check_type_maybe(expected->tt_member, actual->tt_member,
|
||||
FALSE, where);
|
||||
}
|
||||
else if (expected->tt_type == VAR_TUPLE && actual != &t_any)
|
||||
ret = check_tuple_type_maybe(expected, actual, where);
|
||||
else if (expected->tt_type == VAR_FUNC && actual != &t_any)
|
||||
{
|
||||
// If the return type is unknown it can be anything, including
|
||||
@@ -1200,11 +1427,33 @@ skip_type(char_u *start, int optional)
|
||||
// Skip over "<type>"; this is permissive about white space.
|
||||
if (*skipwhite(p) == '<')
|
||||
{
|
||||
p = skipwhite(p);
|
||||
p = skip_type(skipwhite(p + 1), FALSE);
|
||||
p = skipwhite(p);
|
||||
if (*p == '>')
|
||||
++p;
|
||||
if (STRNCMP("tuple", start, 5) == 0)
|
||||
{
|
||||
// handle tuple<{type1}, {type2}, ....<type>>
|
||||
p = skipwhite(p + 1);
|
||||
while (*p != '>' && *p != NUL)
|
||||
{
|
||||
char_u *sp = p;
|
||||
|
||||
if (STRNCMP(p, "...", 3) == 0)
|
||||
p += 3;
|
||||
p = skip_type(p, TRUE);
|
||||
if (p == sp)
|
||||
return p; // syntax error
|
||||
if (*p == ',')
|
||||
p = skipwhite(p + 1);
|
||||
}
|
||||
if (*p == '>')
|
||||
p++;
|
||||
}
|
||||
else
|
||||
{
|
||||
p = skipwhite(p);
|
||||
p = skip_type(skipwhite(p + 1), FALSE);
|
||||
p = skipwhite(p);
|
||||
if (*p == '>')
|
||||
++p;
|
||||
}
|
||||
}
|
||||
else if ((*p == '(' || (*p == ':' && VIM_ISWHITE(p[1])))
|
||||
&& STRNCMP("func", start, 4) == 0)
|
||||
@@ -1422,6 +1671,116 @@ parse_type_func(char_u **arg, size_t len, garray_T *type_gap, int give_error)
|
||||
return type;
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse a "tuple" type at "*arg" and advance over it.
|
||||
* When "give_error" is TRUE give error messages, otherwise be quiet.
|
||||
* Return NULL for failure.
|
||||
*/
|
||||
static type_T *
|
||||
parse_type_tuple(char_u **arg, garray_T *type_gap, int give_error)
|
||||
{
|
||||
char_u *p;
|
||||
type_T *type;
|
||||
type_T *ret_type = NULL;
|
||||
int typecount = -1;
|
||||
int flags = 0;
|
||||
garray_T tuple_types_ga;
|
||||
|
||||
ga_init2(&tuple_types_ga, sizeof(type_T *), 10);
|
||||
|
||||
// tuple<{type}, {type}>
|
||||
// tuple<{type}, ...{type}>
|
||||
if (**arg != '<')
|
||||
{
|
||||
if (give_error)
|
||||
{
|
||||
if (*skipwhite(*arg) == '<')
|
||||
semsg(_(e_no_white_space_allowed_before_str_str), "<", *arg);
|
||||
else
|
||||
semsg(_(e_missing_type_after_str), "tuple");
|
||||
}
|
||||
|
||||
// only "tuple" is specified
|
||||
return NULL;
|
||||
}
|
||||
|
||||
p = ++*arg;
|
||||
typecount = 0;
|
||||
while (*p != NUL && *p != '>')
|
||||
{
|
||||
if (STRNCMP(p, "...", 3) == 0)
|
||||
{
|
||||
flags |= TTFLAG_VARARGS;
|
||||
p += 3;
|
||||
}
|
||||
|
||||
type = parse_type(&p, type_gap, give_error);
|
||||
if (type == NULL)
|
||||
goto on_err;
|
||||
|
||||
if ((flags & TTFLAG_VARARGS) != 0 && type->tt_type != VAR_LIST)
|
||||
{
|
||||
char *tofree;
|
||||
semsg(_(e_variadic_tuple_must_end_with_list_type_str),
|
||||
type_name(type, &tofree));
|
||||
vim_free(tofree);
|
||||
goto on_err;
|
||||
}
|
||||
|
||||
// Add the item type
|
||||
if (ga_grow(&tuple_types_ga, 1) == FAIL)
|
||||
goto on_err;
|
||||
((type_T **)tuple_types_ga.ga_data)[tuple_types_ga.ga_len] = type;
|
||||
tuple_types_ga.ga_len++;
|
||||
typecount++;
|
||||
|
||||
// Nothing comes after "...{type}".
|
||||
if (flags & TTFLAG_VARARGS)
|
||||
break;
|
||||
|
||||
if (*p != ',' && *skipwhite(p) == ',')
|
||||
{
|
||||
if (give_error)
|
||||
semsg(_(e_no_white_space_allowed_before_str_str), ",", p);
|
||||
goto on_err;
|
||||
}
|
||||
if (*p == ',')
|
||||
{
|
||||
++p;
|
||||
if (!VIM_ISWHITE(*p))
|
||||
{
|
||||
if (give_error)
|
||||
semsg(_(e_white_space_required_after_str_str),
|
||||
",", p - 1);
|
||||
goto on_err;
|
||||
}
|
||||
}
|
||||
p = skipwhite(p);
|
||||
}
|
||||
|
||||
p = skipwhite(p);
|
||||
if (*p != '>' || typecount <= 0)
|
||||
{
|
||||
if (give_error)
|
||||
semsg(_(e_missing_type_after_str), p);
|
||||
goto on_err;
|
||||
}
|
||||
*arg = p + 1;
|
||||
|
||||
ret_type = alloc_tuple_type(typecount, type_gap);
|
||||
ret_type->tt_flags = flags;
|
||||
ret_type->tt_argcount = typecount;
|
||||
if (tuple_type_add_types(ret_type, typecount, type_gap) == FAIL)
|
||||
return NULL;
|
||||
mch_memmove(ret_type->tt_args, tuple_types_ga.ga_data,
|
||||
sizeof(type_T *) * typecount);
|
||||
|
||||
on_err:
|
||||
ga_clear(&tuple_types_ga);
|
||||
|
||||
return ret_type;
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse a user defined type at "*arg" and advance over it.
|
||||
* It can be a class or an interface or a typealias name, possibly imported.
|
||||
@@ -1577,6 +1936,13 @@ parse_type(char_u **arg, garray_T *type_gap, int give_error)
|
||||
return &t_string;
|
||||
}
|
||||
break;
|
||||
case 't':
|
||||
if (len == 5 && STRNCMP(*arg, "tuple", len) == 0)
|
||||
{
|
||||
*arg += len;
|
||||
return parse_type_tuple(arg, type_gap, give_error);
|
||||
}
|
||||
break;
|
||||
case 'v':
|
||||
if (len == 4 && STRNCMP(*arg, "void", len) == 0)
|
||||
{
|
||||
@@ -1625,6 +1991,18 @@ equal_type(type_T *type1, type_T *type2, int flags)
|
||||
case VAR_LIST:
|
||||
case VAR_DICT:
|
||||
return equal_type(type1->tt_member, type2->tt_member, flags);
|
||||
case VAR_TUPLE:
|
||||
if (type1->tt_argcount != type2->tt_argcount)
|
||||
return FALSE;
|
||||
if (type1->tt_argcount < 0
|
||||
|| type1->tt_args == NULL || type2->tt_args == NULL)
|
||||
return TRUE;
|
||||
for (i = 0; i < type1->tt_argcount; ++i)
|
||||
if ((flags & ETYPE_ARG_UNKNOWN) == 0
|
||||
&& !equal_type(type1->tt_args[i], type2->tt_args[i],
|
||||
flags))
|
||||
return FALSE;
|
||||
return TRUE;
|
||||
case VAR_FUNC:
|
||||
case VAR_PARTIAL:
|
||||
if (!equal_type(type1->tt_member, type2->tt_member, flags)
|
||||
@@ -1725,7 +2103,8 @@ common_type(type_T *type1, type_T *type2, type_T **dest, garray_T *type_gap)
|
||||
|
||||
if (type1->tt_type == type2->tt_type)
|
||||
{
|
||||
if (type1->tt_type == VAR_LIST || type2->tt_type == VAR_DICT)
|
||||
if (type1->tt_type == VAR_LIST
|
||||
|| type1->tt_type == VAR_DICT)
|
||||
{
|
||||
type_T *common;
|
||||
|
||||
@@ -1736,8 +2115,7 @@ common_type(type_T *type1, type_T *type2, type_T **dest, garray_T *type_gap)
|
||||
*dest = get_dict_type(common, type_gap);
|
||||
return;
|
||||
}
|
||||
|
||||
if (type1->tt_type == VAR_FUNC)
|
||||
else if (type1->tt_type == VAR_FUNC)
|
||||
{
|
||||
common_type_var_func(type1, type2, dest, type_gap);
|
||||
return;
|
||||
@@ -1747,6 +2125,26 @@ common_type(type_T *type1, type_T *type2, type_T **dest, garray_T *type_gap)
|
||||
*dest = &t_any;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the item type of a List, Dict or a Tuple
|
||||
*/
|
||||
type_T *
|
||||
get_item_type(type_T *type)
|
||||
{
|
||||
if (type->tt_type == VAR_TUPLE)
|
||||
{
|
||||
if (type->tt_argcount != 1)
|
||||
return &t_any;
|
||||
|
||||
if (type->tt_flags & TTFLAG_VARARGS)
|
||||
return type->tt_args[0]->tt_member;
|
||||
else
|
||||
return type->tt_args[0];
|
||||
}
|
||||
|
||||
return type->tt_member;
|
||||
}
|
||||
|
||||
/*
|
||||
* Push an entry onto the type stack. "type" used both for the current type
|
||||
* and the declared type.
|
||||
@@ -1864,6 +2262,40 @@ get_member_type_from_stack(
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the types of items in a tuple on the stack of "cctx".
|
||||
* Returns the number of types. Returns -1 on failure.
|
||||
*/
|
||||
int
|
||||
get_tuple_type_from_stack(
|
||||
int count,
|
||||
garray_T *tuple_types_gap,
|
||||
cctx_T *cctx)
|
||||
{
|
||||
garray_T *stack = &cctx->ctx_type_stack;
|
||||
type2_T *typep;
|
||||
type_T *type = NULL;
|
||||
|
||||
// Use "unknown" for an empty tuple
|
||||
if (count == 0)
|
||||
return 0;
|
||||
|
||||
// Find the common type from following items.
|
||||
typep = ((type2_T *)stack->ga_data) + stack->ga_len;
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
type = (typep - (count - i))->type_curr;
|
||||
if (check_type_is_value(type) == FAIL)
|
||||
return -1;
|
||||
if (ga_grow(tuple_types_gap, 1) == FAIL)
|
||||
return -1;
|
||||
((type_T **)tuple_types_gap->ga_data)[tuple_types_gap->ga_len] = type;
|
||||
tuple_types_gap->ga_len++;
|
||||
}
|
||||
|
||||
return tuple_types_gap->ga_len;
|
||||
}
|
||||
|
||||
char *
|
||||
vartype_name(vartype_T type)
|
||||
{
|
||||
@@ -1881,6 +2313,7 @@ vartype_name(vartype_T type)
|
||||
case VAR_JOB: return "job";
|
||||
case VAR_CHANNEL: return "channel";
|
||||
case VAR_LIST: return "list";
|
||||
case VAR_TUPLE: return "tuple";
|
||||
case VAR_DICT: return "dict";
|
||||
case VAR_INSTR: return "instr";
|
||||
case VAR_CLASS: return "class";
|
||||
@@ -1918,6 +2351,65 @@ type_name_list_or_dict(char *name, type_T *type, char **tofree)
|
||||
return *tofree;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the type name of a tuple.
|
||||
* The result may be in allocated memory, in which case "tofree" is set.
|
||||
*/
|
||||
static char *
|
||||
type_name_tuple(type_T *type, char **tofree)
|
||||
{
|
||||
garray_T ga;
|
||||
int i;
|
||||
int varargs = (type->tt_flags & TTFLAG_VARARGS) ? 1 : 0;
|
||||
char *arg_free = NULL;
|
||||
|
||||
ga_init2(&ga, 1, 100);
|
||||
if (ga_grow(&ga, 20) == FAIL)
|
||||
goto failed;
|
||||
STRCPY(ga.ga_data, "tuple<");
|
||||
ga.ga_len += 6;
|
||||
|
||||
if (type->tt_argcount <= 0)
|
||||
// empty tuple
|
||||
ga_concat(&ga, (char_u *)"any");
|
||||
else
|
||||
{
|
||||
if (type->tt_args == NULL)
|
||||
ga_concat(&ga, (char_u *)"[unknown]");
|
||||
else
|
||||
{
|
||||
for (i = 0; i < type->tt_argcount; ++i)
|
||||
{
|
||||
char *arg_type;
|
||||
int len;
|
||||
|
||||
arg_type = type_name(type->tt_args[i], &arg_free);
|
||||
if (i > 0)
|
||||
{
|
||||
STRCPY((char *)ga.ga_data + ga.ga_len, ", ");
|
||||
ga.ga_len += 2;
|
||||
}
|
||||
len = (int)STRLEN(arg_type);
|
||||
if (ga_grow(&ga, len + 8) == FAIL)
|
||||
goto failed;
|
||||
if (varargs && i == type->tt_argcount - 1)
|
||||
ga_concat(&ga, (char_u *)"...");
|
||||
ga_concat(&ga, (char_u *)arg_type);
|
||||
VIM_CLEAR(arg_free);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
STRCPY((char *)ga.ga_data + ga.ga_len, ">");
|
||||
*tofree = ga.ga_data;
|
||||
return ga.ga_data;
|
||||
|
||||
failed:
|
||||
vim_free(arg_free);
|
||||
ga_clear(&ga);
|
||||
return "[unknown]";
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the type name of a Class (class<name>) or Object (object<name>).
|
||||
* The result may be in allocated memory, in which case "tofree" is set.
|
||||
@@ -2035,6 +2527,9 @@ type_name(type_T *type, char **tofree)
|
||||
case VAR_DICT:
|
||||
return type_name_list_or_dict(name, type, tofree);
|
||||
|
||||
case VAR_TUPLE:
|
||||
return type_name_tuple(type, tofree);
|
||||
|
||||
case VAR_CLASS:
|
||||
case VAR_OBJECT:
|
||||
return type_name_class_or_obj(name, type, tofree);
|
||||
|
||||
+20
-4
@@ -1263,6 +1263,7 @@ read_viminfo_varlist(vir_T *virp, int writing)
|
||||
case 'L': type = VAR_LIST; break;
|
||||
case 'B': type = VAR_BLOB; break;
|
||||
case 'X': type = VAR_SPECIAL; break;
|
||||
case 'T': type = VAR_TUPLE; break;
|
||||
}
|
||||
|
||||
tab = vim_strchr(tab, '\t');
|
||||
@@ -1270,7 +1271,8 @@ read_viminfo_varlist(vir_T *virp, int writing)
|
||||
{
|
||||
tv.v_type = type;
|
||||
if (type == VAR_STRING || type == VAR_DICT
|
||||
|| type == VAR_LIST || type == VAR_BLOB)
|
||||
|| type == VAR_LIST || type == VAR_BLOB
|
||||
|| type == VAR_TUPLE)
|
||||
tv.vval.v_string = viminfo_readstring(virp,
|
||||
(int)(tab - virp->vir_line + 1), TRUE);
|
||||
else if (type == VAR_FLOAT)
|
||||
@@ -1282,7 +1284,7 @@ read_viminfo_varlist(vir_T *virp, int writing)
|
||||
|| tv.vval.v_number == VVAL_TRUE))
|
||||
tv.v_type = VAR_BOOL;
|
||||
}
|
||||
if (type == VAR_DICT || type == VAR_LIST)
|
||||
if (type == VAR_DICT || type == VAR_LIST || type == VAR_TUPLE)
|
||||
{
|
||||
typval_T *etv = eval_expr(tv.vval.v_string, NULL);
|
||||
|
||||
@@ -1370,7 +1372,7 @@ write_viminfo_varlist(FILE *fp)
|
||||
|
||||
s = "DIC";
|
||||
if (di != NULL && !set_ref_in_ht(
|
||||
&di->dv_hashtab, copyID, NULL)
|
||||
&di->dv_hashtab, copyID, NULL, NULL)
|
||||
&& di->dv_copyID == copyID)
|
||||
// has a circular reference, can't turn the
|
||||
// value into a string
|
||||
@@ -1384,13 +1386,27 @@ write_viminfo_varlist(FILE *fp)
|
||||
|
||||
s = "LIS";
|
||||
if (l != NULL && !set_ref_in_list_items(
|
||||
l, copyID, NULL)
|
||||
l, copyID, NULL, NULL)
|
||||
&& l->lv_copyID == copyID)
|
||||
// has a circular reference, can't turn the
|
||||
// value into a string
|
||||
continue;
|
||||
break;
|
||||
}
|
||||
case VAR_TUPLE:
|
||||
{
|
||||
tuple_T *tuple = this_var->di_tv.vval.v_tuple;
|
||||
int copyID = get_copyID();
|
||||
|
||||
s = "TUP";
|
||||
if (tuple != NULL && !set_ref_in_tuple_items(
|
||||
tuple, copyID, NULL, NULL)
|
||||
&& tuple->tv_copyID == copyID)
|
||||
// has a circular reference, can't turn the
|
||||
// value into a string
|
||||
continue;
|
||||
break;
|
||||
}
|
||||
case VAR_BLOB: s = "BLO"; break;
|
||||
case VAR_BOOL: s = "XPL"; break; // backwards compat.
|
||||
case VAR_SPECIAL: s = "XPL"; break;
|
||||
|
||||
Reference in New Issue
Block a user