Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: lambdas and parens, multiple indents #347

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 1 addition & 11 deletions gdtoolkit/parser/gdscript.lark
Original file line number Diff line number Diff line change
Expand Up @@ -255,18 +255,8 @@ _lambda_suite: _lambda_body
| _standalone_lambda_stmt
_lambda_body: _NL _INDENT _func_stmt+ _DEDENT
_standalone_lambda_stmt: _simple_func_stmt
// | annotation* compound_lambda_stmt
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

opinionated, but I think fixing compound func stmt is the way to go and this works some of the time. could def revert this part tho

// | annotation* compound_func_stmt
| annotation* compound_func_stmt
| annotation*
// ?compound_lambda_stmt: lambda_if_stmt
// | while_stmt
// | for_stmt
// | for_stmt_typed
// | match_stmt
// lambda_if_stmt: lambda_if_branch (_NL? lambda_elif_branch)* [_NL? lambda_else_branch]
// lambda_if_branch: "if" expr ":" _lambda_suite
// lambda_elif_branch: "elif" expr ":" _lambda_suite
// lambda_else_branch: "else" ":" _lambda_suite
?literal: NUMBER
| string
| rstring
Expand Down
38 changes: 24 additions & 14 deletions gdtoolkit/parser/gdscript_indenter.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ class GDScriptIndenter(Indenter):
NL_type = "_NL"
OPEN_PAREN_types = ["LPAR", "LSQB", "LBRACE"]
CLOSE_PAREN_types = ["RPAR", "RSQB", "RBRACE"]
LAMBDA_LINE_EXTENSION_types = ["IF", "WHILE", "FOR", "MATCH"]
LAMBDA_SEPARATOR_types = ["COMMA"]
INDENT_type = "_INDENT"
DEDENT_type = "_DEDENT"
Expand Down Expand Up @@ -42,51 +43,60 @@ def _process(self, stream):
self.processed_tokens = []
self.undedented_lambdas_at_paren_level = defaultdict(int)

had_newline = False
for produced_token in super()._process(self._record_stream(stream)):
if (
produced_token.type in self.CLOSE_PAREN_types
or produced_token.type in self.LAMBDA_SEPARATOR_types
):
# dedenting all undedented lambas (more than one if nested) at current paren level
while self.undedented_lambdas_at_paren_level[self.paren_level] > 0:
yield from self._dedent_lambda_at_token(produced_token)
yield from self._dedent_lambda_at_token(had_newline, produced_token)
had_newline = False
had_newline = produced_token.type == self.NL_type
yield produced_token

def _record_stream(self, stream):
for token in stream:
self.processed_tokens.append(token)
yield token

def _in_multiline_lambda(self):
return self.undedented_lambdas_at_paren_level[self.paren_level] > 0

# pylint: disable=invalid-name
def _handle_NL_in_parens(self, token: Token):
# Adapted from lark/indendeter.py as that normally disables line handling
# when paren_level > 0.
# NOTE: we never raise DedentError here as it doesn't make sense in parens
indent_str = token.rsplit("\n", 1)[1] # tabs and spaces
indent = indent_str.count(" ") + indent_str.count("\t") * self.tab_len

if (
if indent > self.indent_level[-1] and (
self._current_token_is_just_after_lambda_header()
and indent > self.indent_level[-1]
or self._in_multiline_lambda()
):
self.indent_level.append(indent)
self.undedented_lambdas_at_paren_level[self.paren_level] += 1
yield token
yield Token.new_borrow_pos(self.INDENT_type, indent_str, token)
elif (
indent <= self.indent_level[-1]
and self.undedented_lambdas_at_paren_level[self.paren_level] > 0
):
elif indent <= self.indent_level[-1] and self._in_multiline_lambda():
yield token

while indent < self.indent_level[-1]:
while indent < self.indent_level[-1] and self._in_multiline_lambda():
self.indent_level.pop()
self.undedented_lambdas_at_paren_level[self.paren_level] -= 1
yield Token.new_borrow_pos(self.DEDENT_type, indent_str, token)

# never raising DedentError here as it doesn't make sense in parens
yield Token(self.DEDENT_type, None, None, token.line, None, token.line)
# If we are still in a situation that can handle newlines, emit an extra
# one with the same rationale as above
if self._in_multiline_lambda():
yield token
# Otherwise do nothing as other expressions don't handle newlines

def _dedent_lambda_at_token(self, token: Token):
def _dedent_lambda_at_token(self, had_newline: bool, token: Token):
self.indent_level.pop()
self.undedented_lambdas_at_paren_level[self.paren_level] -= 1
yield Token.new_borrow_pos(self.NL_type, "N/A", token)
if not had_newline:
yield Token.new_borrow_pos(self.NL_type, "N/A", token)
yield Token.new_borrow_pos(self.DEDENT_type, "N/A", token)

def _current_token_is_just_after_lambda_header(self):
Expand Down
10 changes: 10 additions & 0 deletions tests/valid-gd-scripts/multiline_lambdas.gd
Original file line number Diff line number Diff line change
@@ -1,4 +1,14 @@
func foo():
stack(func():
print("foo")
if source == 1:
var x: = 1
pass)
stack(func():
if true:
pass
if false:
pass)
var f0 = func bar():
pass
var f1 = func():
Expand Down
Loading