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

Parse error in multiline attribute expression #35415

Closed
Tracked by #40488
Scony opened this issue Jan 21, 2020 · 10 comments
Closed
Tracked by #40488

Parse error in multiline attribute expression #35415

Scony opened this issue Jan 21, 2020 · 10 comments

Comments

@Scony
Copy link
Contributor

Scony commented Jan 21, 2020

Godot version:
master @ d11d7df

OS/device including version:
Linux

Issue description:
Godot raises parse error on the code below:

class X:
	func foo(p):
		var x = (
			p
			.some
			.some
			.some
		)
Godot Engine v3.2.rc.custom_build.d11d7dfe3 - https://godotengine.org
 
SCRIPT ERROR: GDScript::reload: Parse Error: Expected ')' in expression
   At: res://tests/potential-godot-bugs/multiline-attribute-expression.gd:5.
ERROR: reload: Method/Function Failed, returning: ERR_PARSE_ERROR
   At: modules/gdscript/gdscript.cpp:576.
@Calinou
Copy link
Member

Calinou commented Jan 21, 2020

I think the only way to chain methods on newlines right now is to add backslashes at the end of every line.

@Scony
Copy link
Contributor Author

Scony commented Jan 22, 2020

Well, this is a bit counter-intuitive since python (which GDScript is motivated by) has no problems with such a code. Also, it's useful for formatting in case of such a long attr/call chains.

@Calinou
Copy link
Member

Calinou commented Jan 22, 2020

The issue is that the .method() syntax is used to call a method on the parent class. Therefore, it'd be ambiguous to support chaining methods without a backslash (unless we change the parent method call syntax).

@Scony
Copy link
Contributor Author

Scony commented Jan 22, 2020

Actually it doesn't matter. You cannot have an attribute expression starting with a dot (like .aaa.bbb.ccc). And hence there is no ambiguity - from pure parsing perspective, the expression starting with dot must be a parent call. And please note that newline should not matter inside of any expression (maybe except long string """xxx""").

@Calinou
Copy link
Member

Calinou commented Jan 22, 2020

@Scony Right, so we can support it for property access but not for method calls. That's pretty inconsistent, so I'm still not sure if we should support that 🙁

@Scony
Copy link
Contributor Author

Scony commented Jan 22, 2020

@Calinou what do you mean by but not for method calls?

@dalexeev
Copy link
Member

dalexeev commented Jan 23, 2020

var x = (
    p
    .some
    .some
    .some
)

I am not a fan of this style of code, but specifically in this case, the parser is wrong. Since the expression is enclosed in parentheses, indents should be treated as spaces:

var x = (p .some .some .some)

Probably the problem is that in the current implementation of the parser, indentation is treated the same way, regardless of context (operator stream or expression).

ADD. The same code, but without parentheses, I think, should really cause a parse error:

var x = p
    .some
    .some
    .some

@bojidar-bg
Copy link
Contributor

bojidar-bg commented Jan 24, 2020

Probably the problem is that in the current implementation of the parser, indentation is treated the same way, regardless of context (operator stream or expression).

Actually the problem is simpler: The code in the parser currently does not try parsing newlines interspersed in indexing operations, the way the code related to parsing parts of an expression does. (and, no, x.y is not really an operator, since y is not a valid expression on its own)

Code not checking for a newline in x<newline>.y

while (true) {
//expressions can be indexed any number of times
if (tokenizer->get_token() == GDScriptTokenizer::TK_PERIOD) {

Code checking for a newline in x +<newline>y:

if (parenthesis > 0) {
//remove empty space (only allowed if inside parenthesis
while (tokenizer->get_token() == GDScriptTokenizer::TK_NEWLINE) {
tokenizer->advance();
}
}

Code checking for a newline in x<newline>+ y:

if (parenthesis > 0) {
//remove empty space (only allowed if inside parenthesis
while (tokenizer->get_token() == GDScriptTokenizer::TK_NEWLINE) {
tokenizer->advance();
}
}

Keep in mind that a similar check would be needed for p.<newline>some, and maybe p.some<newline>(.

Finally, note that the following is valid syntax:

func a():
    var b = c # Important: no parentheses around c<newline>.d()
    .d()

It stands for a call to d() in the base/super class.

@JohnCorby
Copy link

cant this pretty easily be fixed with 4.0 since it will use super.thing instead of just .thing to get superclass methods/fields? when that happens, doing this chaining (with or without parenthesis) wont be ambiguous for the parser, right?

@KoBeWi
Copy link
Member

KoBeWi commented Dec 17, 2020

Fixed in 4.0

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

6 participants