Skip to content

Commit

Permalink
Add support for Godot 4.2 typed for loop iterator variable, fixes #236 (
Browse files Browse the repository at this point in the history
  • Loading branch information
neilgoodman authored Oct 25, 2023
1 parent fc94c28 commit 62e429a
Show file tree
Hide file tree
Showing 11 changed files with 68 additions and 1 deletion.
3 changes: 3 additions & 0 deletions gdtoolkit/common/ast.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ def __init__(self, node: Tree, annotations: List[Annotation] = []):

self._load_sub_statements()

# pylint: disable=too-many-branches
def _load_sub_statements(self):
if self.kind == "class_def":
pass # TODO: implement
Expand All @@ -48,6 +49,8 @@ def _load_sub_statements(self):
self.sub_statements = [Statement(n) for n in self.lark_node.children[1:]]
elif self.kind == "for_stmt":
self.sub_statements = [Statement(n) for n in self.lark_node.children[2:]]
elif self.kind == "for_stmt_typed":
self.sub_statements = [Statement(n) for n in self.lark_node.children[3:]]
elif self.kind == "match_stmt":
for branch in self.lark_node.children:
self.sub_statements += [Statement(n) for n in branch.children[1:]]
Expand Down
8 changes: 8 additions & 0 deletions gdtoolkit/formatter/function_statement.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ def format_func_statement(statement: Tree, context: Context) -> Outcome:
"if_stmt": _format_if_statement,
"while_stmt": partial(_format_branch, "while ", ":", 0),
"for_stmt": _format_for_statement,
"for_stmt_typed": _format_for_statement_typed,
"match_stmt": _format_match_statement,
"annotation": format_standalone_annotation,
# fake statements:
Expand Down Expand Up @@ -83,6 +84,13 @@ def _format_for_statement(statement: Tree, context: Context) -> Outcome:
return _format_branch(prefix, suffix, expr_position, statement, context)


def _format_for_statement_typed(statement: Tree, context: Context) -> Outcome:
prefix = f"for {statement.children[0].value}: {statement.children[1].value} in "
suffix = ":"
expr_position = 2
return _format_branch(prefix, suffix, expr_position, statement, context)


def _format_match_statement(statement: Tree, context: Context) -> Outcome:
prefix = "match "
suffix = ":"
Expand Down
8 changes: 8 additions & 0 deletions gdtoolkit/gd2py/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,14 @@ def _convert_statement(statement: Tree, context: Context) -> List[str]:
)
]
+ _convert_block(s.children[2:], c.create_child_context(-1)),
"for_stmt_typed": lambda s, c: [
"{}for {} in {}:".format(
c.indent_string,
s.children[0].value,
_convert_expression_to_str(s.children[2]),
)
]
+ _convert_block(s.children[3:], c.create_child_context(-2)),
"match_stmt": _convert_match_statement,
"match_branch": partial(_convert_branch_with_expression, "elif"),
} # type: Dict[str, Callable]
Expand Down
3 changes: 2 additions & 1 deletion gdtoolkit/linter/name_checks.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ def lint(parse_tree: Tree, config: MappingProxyType) -> List[Problem]:
"enum_named",
"enum_element",
"for_stmt",
"for_stmt_typed",
"func_arg_regular",
"func_arg_inf",
"func_arg_typed",
Expand Down Expand Up @@ -93,7 +94,7 @@ def lint(parse_tree: Tree, config: MappingProxyType) -> List[Problem]:
partial(
_generic_name_check,
config["loop-variable-name"],
rule_name_tokens["for_stmt"],
rule_name_tokens["for_stmt"] + rule_name_tokens["for_stmt_typed"],
"loop-variable-name",
'Loop variable name "{}" is not valid',
),
Expand Down
3 changes: 3 additions & 0 deletions gdtoolkit/parser/gdscript.lark
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ _var_typed: "var" NAME ":" TYPE_HINT
class_var_typed: _var_typed [inline_property_body]
class_var_typed_assgnd: _var_typed "=" expr [inline_property_body]
class_var_inf: "var" NAME ":" "=" expr [inline_property_body]
_for_iterator_var_typed: NAME ":" TYPE_HINT
inline_property_body: ":" [property_delegate_set ["," property_delegate_get]]
| ":" [property_delegate_get ["," property_delegate_set]]
const_stmt: const_assigned
Expand Down Expand Up @@ -95,6 +96,7 @@ _simple_func_stmt: annotation* single_func_stmt (";" annotation* single_func_stm
?compound_func_stmt: if_stmt
| while_stmt
| for_stmt
| for_stmt_typed
| match_stmt
return_stmt: "return" [expr]
func_var_stmt: func_var_empty
Expand All @@ -117,6 +119,7 @@ elif_branch: "elif" expr ":" _func_suite
else_branch: "else" ":" _func_suite
while_stmt: "while" expr ":" _func_suite
for_stmt: "for" NAME "in" expr ":" _func_suite
for_stmt_typed: "for" _for_iterator_var_typed "in" expr ":" _func_suite
match_stmt: "match" expr ":" _match_body
_match_body: _NL _INDENT match_branch+ _DEDENT
match_branch: pattern ":" _func_suite
Expand Down
5 changes: 5 additions & 0 deletions tests/formatter/input-output-pairs/for_loop_typed.in.gd
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class X:
func foo():
var a: Array[int] = [1]
for i:int in a:
print(a[i])
5 changes: 5 additions & 0 deletions tests/formatter/input-output-pairs/for_loop_typed.out.gd
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class X:
func foo():
var a: Array[int] = [1]
for i: int in a:
print(a[i])
2 changes: 2 additions & 0 deletions tests/gd2py/input-output-pairs/func_level_statements.in.gd
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ func foo():
break
for i in 1:
continue
for j: int in 1:
continue
match 1:
1:
pass
Expand Down
2 changes: 2 additions & 0 deletions tests/gd2py/input-output-pairs/func_level_statements.out.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ def foo():
break
for i in 1:
continue
for j in 1:
continue
if 1:
pass
elif 1:
Expand Down
24 changes: 24 additions & 0 deletions tests/linter/test_name_checks.py
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,18 @@ def test_enum_element_name_nok(code):
for aaa_bbb in y:
pass
""",
"""func foo():
for _x: int in y:
pass
""",
"""func foo():
for xyz: int in y:
pass
""",
"""func foo():
for aaa_bbb: int in y:
pass
""",
])
def test_loop_variable_name_ok(code):
simple_ok_check(code)
Expand All @@ -211,6 +223,18 @@ def test_loop_variable_name_ok(code):
for X_X in y:
pass
""",
"""func foo():
for x_: int in y:
pass
""",
"""func foo():
for xX: int in y:
pass
""",
"""func foo():
for X_X: int in y:
pass
""",
])
def test_loop_variable_name_nok(code):
simple_nok_check(code, 'loop-variable-name')
Expand Down
6 changes: 6 additions & 0 deletions tests/valid-gd-scripts/typed_for_loop.gd
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
extends Node

func _ready():
var a: Array[int] = [1]
for i: int in a:
print(a)

0 comments on commit 62e429a

Please sign in to comment.