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:
Yegappan Lakshmanan
2025-03-23 16:42:16 +01:00
committed by Christian Brabandt
parent adb703e1b9
commit 9cb865e95b
75 changed files with 7155 additions and 691 deletions
+2
View File
@@ -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
View File
@@ -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
View File
@@ -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
+40
View File
@@ -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*
+6 -1
View File
@@ -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
View File
@@ -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
+6 -1
View File
@@ -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
View File
@@ -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
+1
View File
@@ -169,6 +169,7 @@ SRC += \
textobject.c \
textprop.c \
time.c \
tuple.c \
typval.c \
ui.c \
undo.c \
+1
View File
@@ -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 \
+4
View File
@@ -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 \
+5
View File
@@ -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
+11
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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), &ltv, FALSE,
flags | ASSIGN_UNPACK, (char_u *)"]", op, var_idx);
flags | ASSIGN_UNPACK, (char_u *)"]", op, var_idx);
clear_tv(&ltv);
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
View File
@@ -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 != '=')
{
+142 -43
View File
@@ -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
View File
@@ -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
View File
@@ -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:
+1 -1
View File
@@ -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
View File
@@ -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
View File
@@ -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
+4
View File
@@ -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
View File
@@ -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
View File
@@ -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;
+1
View File
@@ -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"
+1
View File
@@ -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);
+1
View File
@@ -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
View File
@@ -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 : */
+2
View File
@@ -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);
+1
View File
@@ -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);
+34
View File
@@ -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 : */
+9 -6
View File
@@ -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);
+1
View File
@@ -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);
+3
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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;
}
+2
View File
@@ -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 \
+2 -1
View File
@@ -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))
+3 -3
View File
@@ -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)
+4 -4
View File
@@ -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()
+1
View File
@@ -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()
+1 -1
View File
@@ -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()
+1 -1
View File
@@ -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:')
+17 -9
View File
@@ -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)
+2 -2
View File
@@ -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())
+8
View File
@@ -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
+1 -1
View File
@@ -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]
+19 -15
View File
@@ -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()
+180
View File
@@ -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
+10
View File
@@ -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
View File
@@ -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
+10
View File
@@ -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
View File
@@ -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
View File
File diff suppressed because it is too large Load Diff
+157 -20
View File
@@ -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
View File
@@ -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;
}
+2
View File
@@ -704,6 +704,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
1232,
/**/
1231,
/**/
+3 -1
View File
@@ -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
+6
View File
@@ -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
View File
@@ -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;
}
+37
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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:
+1
View File
@@ -1127,6 +1127,7 @@ static char *reserved[] = {
"null_dict",
"null_function",
"null_list",
"null_tuple",
"null_partial",
"null_string",
"null_channel",
+505 -10
View File
@@ -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
View File
@@ -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;