Skip to content

Commit 4be5754

Browse files
authored
add support for array and string intrinsic functions (#7655)
1 parent 8eaabbd commit 4be5754

File tree

64 files changed

+8734
-73
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

64 files changed

+8734
-73
lines changed

localstack/services/stepfunctions/asl/component/intrinsic/function/statesfunction/array/__init__.py

Whitespace-only changes.
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
from typing import Any
2+
3+
from localstack.services.stepfunctions.asl.component.intrinsic.argument.function_argument_list import (
4+
FunctionArgumentList,
5+
)
6+
from localstack.services.stepfunctions.asl.component.intrinsic.function.statesfunction.states_function import (
7+
StatesFunction,
8+
)
9+
from localstack.services.stepfunctions.asl.component.intrinsic.functionname.state_function_name_types import (
10+
StatesFunctionNameType,
11+
)
12+
from localstack.services.stepfunctions.asl.component.intrinsic.functionname.states_function_name import (
13+
StatesFunctionName,
14+
)
15+
from localstack.services.stepfunctions.asl.eval.environment import Environment
16+
17+
18+
class Array(StatesFunction):
19+
def __init__(self, arg_list: FunctionArgumentList):
20+
super().__init__(
21+
states_name=StatesFunctionName(function_type=StatesFunctionNameType.Array),
22+
arg_list=arg_list,
23+
)
24+
25+
def _eval_body(self, env: Environment) -> None:
26+
self.arg_list.eval(env=env)
27+
values: list[Any] = list()
28+
for _ in range(self.arg_list.size):
29+
values.append(env.stack.pop())
30+
values.reverse()
31+
env.stack.append(values)
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
from localstack.services.stepfunctions.asl.component.intrinsic.argument.function_argument_list import (
2+
FunctionArgumentList,
3+
)
4+
from localstack.services.stepfunctions.asl.component.intrinsic.function.statesfunction.states_function import (
5+
StatesFunction,
6+
)
7+
from localstack.services.stepfunctions.asl.component.intrinsic.functionname.state_function_name_types import (
8+
StatesFunctionNameType,
9+
)
10+
from localstack.services.stepfunctions.asl.component.intrinsic.functionname.states_function_name import (
11+
StatesFunctionName,
12+
)
13+
from localstack.services.stepfunctions.asl.eval.environment import Environment
14+
15+
16+
class ArrayContains(StatesFunction):
17+
# Determines if a specific value is present in an array.
18+
#
19+
# For example:
20+
# With input
21+
# {
22+
# "inputArray": [1,2,3,4,5,6,7,8,9],
23+
# "lookingFor": 5
24+
# }
25+
#
26+
# The call:
27+
# States.ArrayContains($.inputArray, $.lookingFor)
28+
#
29+
# Returns:
30+
# true
31+
32+
def __init__(self, arg_list: FunctionArgumentList):
33+
super().__init__(
34+
states_name=StatesFunctionName(function_type=StatesFunctionNameType.ArrayContains),
35+
arg_list=arg_list,
36+
)
37+
if arg_list.size != 2:
38+
raise ValueError(
39+
f"Expected 2 arguments for function type '{type(self)}', but got: '{arg_list}'."
40+
)
41+
42+
def _eval_body(self, env: Environment) -> None:
43+
self.arg_list.eval(env=env)
44+
value = env.stack.pop()
45+
array = env.stack.pop()
46+
if not isinstance(array, list):
47+
raise TypeError(f"Expected an array type as first argument, but got {array}.")
48+
contains = value in array
49+
env.stack.append(contains)
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
from localstack.services.stepfunctions.asl.component.intrinsic.argument.function_argument_list import (
2+
FunctionArgumentList,
3+
)
4+
from localstack.services.stepfunctions.asl.component.intrinsic.function.statesfunction.states_function import (
5+
StatesFunction,
6+
)
7+
from localstack.services.stepfunctions.asl.component.intrinsic.functionname.state_function_name_types import (
8+
StatesFunctionNameType,
9+
)
10+
from localstack.services.stepfunctions.asl.component.intrinsic.functionname.states_function_name import (
11+
StatesFunctionName,
12+
)
13+
from localstack.services.stepfunctions.asl.eval.environment import Environment
14+
15+
16+
class ArrayGetItem(StatesFunction):
17+
# Returns a specified index's value.
18+
#
19+
# For example:
20+
# With input
21+
# {
22+
# "inputArray": [1,2,3,4,5,6,7,8,9],
23+
# "index": 5
24+
# }
25+
#
26+
# The call
27+
# States.ArrayGetItem($.inputArray, $.index)
28+
#
29+
# Returns
30+
# 6
31+
32+
def __init__(self, arg_list: FunctionArgumentList):
33+
super().__init__(
34+
states_name=StatesFunctionName(function_type=StatesFunctionNameType.ArrayGetItem),
35+
arg_list=arg_list,
36+
)
37+
if arg_list.size != 2:
38+
raise ValueError(
39+
f"Expected 2 arguments for function type '{type(self)}', but got: '{arg_list}'."
40+
)
41+
42+
def _eval_body(self, env: Environment) -> None:
43+
self.arg_list.eval(env=env)
44+
45+
index = env.stack.pop()
46+
if not isinstance(index, int):
47+
raise TypeError(f"Expected an integer index value, but got '{index}'.")
48+
49+
array = env.stack.pop()
50+
if not isinstance(array, list):
51+
raise TypeError(f"Expected an array type, but got '{array}'.")
52+
53+
item = array[index]
54+
env.stack.append(item)
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
from localstack.services.stepfunctions.asl.component.intrinsic.argument.function_argument_list import (
2+
FunctionArgumentList,
3+
)
4+
from localstack.services.stepfunctions.asl.component.intrinsic.function.statesfunction.states_function import (
5+
StatesFunction,
6+
)
7+
from localstack.services.stepfunctions.asl.component.intrinsic.functionname.state_function_name_types import (
8+
StatesFunctionNameType,
9+
)
10+
from localstack.services.stepfunctions.asl.component.intrinsic.functionname.states_function_name import (
11+
StatesFunctionName,
12+
)
13+
from localstack.services.stepfunctions.asl.eval.environment import Environment
14+
15+
16+
class ArrayLength(StatesFunction):
17+
# Returns the length of the array.
18+
#
19+
# For example:
20+
# With input
21+
# {
22+
# "inputArray": [1,2,3,4,5,6,7,8,9]
23+
# }
24+
#
25+
# The call
26+
# States.ArrayLength($.inputArray)
27+
#
28+
# Returns
29+
# 9
30+
31+
def __init__(self, arg_list: FunctionArgumentList):
32+
super().__init__(
33+
states_name=StatesFunctionName(function_type=StatesFunctionNameType.ArrayLength),
34+
arg_list=arg_list,
35+
)
36+
if arg_list.size != 1:
37+
raise ValueError(
38+
f"Expected 1 argument for function type '{type(self)}', but got: '{arg_list}'."
39+
)
40+
41+
def _eval_body(self, env: Environment) -> None:
42+
self.arg_list.eval(env=env)
43+
44+
array = env.stack.pop()
45+
if not isinstance(array, list):
46+
raise TypeError(f"Expected an array type, but got '{array}'.")
47+
48+
length = len(array)
49+
env.stack.append(length)
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
from localstack.services.stepfunctions.asl.component.intrinsic.argument.function_argument_list import (
2+
FunctionArgumentList,
3+
)
4+
from localstack.services.stepfunctions.asl.component.intrinsic.function.statesfunction.states_function import (
5+
StatesFunction,
6+
)
7+
from localstack.services.stepfunctions.asl.component.intrinsic.functionname.state_function_name_types import (
8+
StatesFunctionNameType,
9+
)
10+
from localstack.services.stepfunctions.asl.component.intrinsic.functionname.states_function_name import (
11+
StatesFunctionName,
12+
)
13+
from localstack.services.stepfunctions.asl.eval.environment import Environment
14+
15+
16+
class ArrayPartition(StatesFunction):
17+
# Partitions the input array.
18+
#
19+
# For example:
20+
# With input
21+
# {
22+
# "inputArray": [1, 2, 3, 4, 5, 6, 7, 8, 9]
23+
# }
24+
#
25+
# The call
26+
# States.ArrayPartition($.inputArray,4)
27+
#
28+
# Returns
29+
# [ [1,2,3,4], [5,6,7,8], [9]]
30+
31+
def __init__(self, arg_list: FunctionArgumentList):
32+
super().__init__(
33+
states_name=StatesFunctionName(function_type=StatesFunctionNameType.ArrayPartition),
34+
arg_list=arg_list,
35+
)
36+
if arg_list.size != 2:
37+
raise ValueError(
38+
f"Expected 2 arguments for function type '{type(self)}', but got: '{arg_list}'."
39+
)
40+
41+
def _eval_body(self, env: Environment) -> None:
42+
self.arg_list.eval(env=env)
43+
chunk_size = env.stack.pop()
44+
if not isinstance(chunk_size, (int, float)):
45+
raise TypeError(f"Expected an integer value as chunk_size, but got {chunk_size}.")
46+
chunk_size = round(chunk_size)
47+
if chunk_size < 0:
48+
raise ValueError(
49+
f"Expected a non-zero, positive integer as chuck_size, but got {chunk_size}."
50+
)
51+
52+
array = env.stack.pop()
53+
if not isinstance(array, list):
54+
raise TypeError(f"Expected an array type as first argument, but got {array}.")
55+
56+
chunks = self._to_chunks(array=array, chunk_size=chunk_size)
57+
env.stack.append(chunks)
58+
59+
@staticmethod
60+
def _to_chunks(array: list, chunk_size: int):
61+
chunks = list()
62+
for i in range(0, len(array), chunk_size):
63+
chunks.append(array[i : i + chunk_size])
64+
return chunks
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
from localstack.services.stepfunctions.asl.component.intrinsic.argument.function_argument_list import (
2+
FunctionArgumentList,
3+
)
4+
from localstack.services.stepfunctions.asl.component.intrinsic.function.statesfunction.states_function import (
5+
StatesFunction,
6+
)
7+
from localstack.services.stepfunctions.asl.component.intrinsic.functionname.state_function_name_types import (
8+
StatesFunctionNameType,
9+
)
10+
from localstack.services.stepfunctions.asl.component.intrinsic.functionname.states_function_name import (
11+
StatesFunctionName,
12+
)
13+
from localstack.services.stepfunctions.asl.eval.environment import Environment
14+
15+
16+
class ArrayRange(StatesFunction):
17+
# Creates a new array containing a specific range of elements.
18+
#
19+
# For example:
20+
# The call
21+
# States.ArrayRange(1, 9, 2)
22+
#
23+
# Returns
24+
# [1,3,5,7,9]
25+
26+
def __init__(self, arg_list: FunctionArgumentList):
27+
super().__init__(
28+
states_name=StatesFunctionName(function_type=StatesFunctionNameType.ArrayRange),
29+
arg_list=arg_list,
30+
)
31+
if arg_list.size != 3:
32+
raise ValueError(
33+
f"Expected 3 arguments for function type '{type(self)}', but got: '{arg_list}'."
34+
)
35+
36+
def _eval_body(self, env: Environment) -> None:
37+
self.arg_list.eval(env=env)
38+
range_vals = [env.stack.pop(), env.stack.pop(), env.stack.pop()]
39+
for range_val in range_vals:
40+
if not isinstance(range_val, (int, float)):
41+
raise TypeError(
42+
f"Expected 3 integer arguments for function type '{type(self)}', but got: '{range_vals}'."
43+
)
44+
step = round(range_vals[0])
45+
last = round(range_vals[1])
46+
first = round(range_vals[2])
47+
48+
if step <= 0:
49+
raise ValueError(f"Expected step argument to be non negative, but got: '{step}'.")
50+
51+
array = list(range(first, last + 1, step))
52+
53+
if len(array) > 1000:
54+
raise ValueError(f"Arrays cannot contain more than 1000 items, size: {len(array)}.")
55+
56+
env.stack.append(array)
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
from collections import OrderedDict
2+
3+
from localstack.services.stepfunctions.asl.component.intrinsic.argument.function_argument_list import (
4+
FunctionArgumentList,
5+
)
6+
from localstack.services.stepfunctions.asl.component.intrinsic.function.statesfunction.states_function import (
7+
StatesFunction,
8+
)
9+
from localstack.services.stepfunctions.asl.component.intrinsic.functionname.state_function_name_types import (
10+
StatesFunctionNameType,
11+
)
12+
from localstack.services.stepfunctions.asl.component.intrinsic.functionname.states_function_name import (
13+
StatesFunctionName,
14+
)
15+
from localstack.services.stepfunctions.asl.eval.environment import Environment
16+
17+
18+
class ArrayUnique(StatesFunction):
19+
# Removes duplicate values from an array and returns an array containing only unique elements
20+
#
21+
# For example:
22+
# With input
23+
# {
24+
# "inputArray": [1,2,3,3,3,3,3,3,4]
25+
# }
26+
#
27+
# The call
28+
# States.ArrayUnique($.inputArray)
29+
#
30+
# Returns
31+
# [1,2,3,4]
32+
33+
def __init__(self, arg_list: FunctionArgumentList):
34+
super().__init__(
35+
states_name=StatesFunctionName(function_type=StatesFunctionNameType.ArrayUnique),
36+
arg_list=arg_list,
37+
)
38+
if arg_list.size != 1:
39+
raise ValueError(
40+
f"Expected 1 argument for function type '{type(self)}', but got: '{arg_list}'."
41+
)
42+
43+
def _eval_body(self, env: Environment) -> None:
44+
self.arg_list.eval(env=env)
45+
46+
array = env.stack.pop()
47+
if not isinstance(array, list):
48+
raise TypeError(f"Expected an array type, but got '{array}'.")
49+
50+
# Remove duplicates through an ordered set, in this
51+
# case we consider they key set of an ordered dict.
52+
items_odict = OrderedDict.fromkeys(array).keys()
53+
unique_array = list(items_odict)
54+
env.stack.append(unique_array)

0 commit comments

Comments
 (0)