Godot: Can't call methods or access properties with names identical to GDScript keywords

Created on 20 Mar 2017  路  5Comments  路  Source: godotengine/godot

*bugsquad edit: * Original title was: "Can't call VisualServer.sync()"

Operating system or device - Godot version:
Godot 2.2 / 3.0

Issue description:

VisualServer.flush() is renamed to sync() at b524b40fdc5325c840192ce92dbed8108ccef2d9
And sync became a keyward for high level network function.
So, now, VS.sync() makes error Expected identifier as member

Steps to reproduce:

func _ready():
    VS.sync()
    VS.call("sync") # workaround

Link to minimal example project:

bug discussion gdscript

Most helpful comment

Just to note -- because of similar issues it is:

  • Impossible to make a function called log (for a logger script)
  • Probably impossible to use dictionary.log, dictionary.master, dictionary.slave, dictionary.signal, etc.
  • Probably impossible to do var dict = { master = get_node("master") } (easy to workaround tho).

So :+1: for that, it is a nuisance when doing normal stuff.


On to a more serious matter -- fixing it:
Currently, GDTokenizer would mark it as a keyword, not an identifier, and thus everything in GDParser which expects an identifier won't be able to use it.

One "simple" way to fix it would be to remove all references to TK_IDENTIFIER, and replace them by a new bool GDTokenizer::is_token_identifier(int).
The other way would be to remove most keywords from GDTokenizer, and then check the keywords either in some new GDTokenier::is_keyword(..) or directly in GDParser. This would lead to harder syntax highlighting for them, though.


Finally, there is a small question with both of those approaches. Consider the following GDScript:

extends Node # logger.gd

func log(prefix, string):
  print("[%s] %s" % [prefix, string])

func _ready():
  log("system", "Logger initialized!") # Ouch!

Should the log call in _ready call logger.gd::log or @GDScript::log? Or should declaring a function called log be forbidden? Here are some considerations:

  • Call logger.gd::log -- if we go with this, there is now officially no way to call the real "log", unless we expose @GDScript directly (which isn't that bad an idea, actually). You can "vote" for it with :tada:
  • Call @GDScript::log -- if we do this, we can still call logger.gd::log using self.log or (if it is a singleton) Logger.log. You can "vote" for it with :heart:
  • Forbid func log -- if we do this, there is now no way to make a function called log (which would still be allowed in non-GDScript Scripts), unless we add func "log"() or func \log(), both of which are somewhat ugly. You can "vote" for it with :smile:

All 5 comments

Just to note -- because of similar issues it is:

  • Impossible to make a function called log (for a logger script)
  • Probably impossible to use dictionary.log, dictionary.master, dictionary.slave, dictionary.signal, etc.
  • Probably impossible to do var dict = { master = get_node("master") } (easy to workaround tho).

So :+1: for that, it is a nuisance when doing normal stuff.


On to a more serious matter -- fixing it:
Currently, GDTokenizer would mark it as a keyword, not an identifier, and thus everything in GDParser which expects an identifier won't be able to use it.

One "simple" way to fix it would be to remove all references to TK_IDENTIFIER, and replace them by a new bool GDTokenizer::is_token_identifier(int).
The other way would be to remove most keywords from GDTokenizer, and then check the keywords either in some new GDTokenier::is_keyword(..) or directly in GDParser. This would lead to harder syntax highlighting for them, though.


Finally, there is a small question with both of those approaches. Consider the following GDScript:

extends Node # logger.gd

func log(prefix, string):
  print("[%s] %s" % [prefix, string])

func _ready():
  log("system", "Logger initialized!") # Ouch!

Should the log call in _ready call logger.gd::log or @GDScript::log? Or should declaring a function called log be forbidden? Here are some considerations:

  • Call logger.gd::log -- if we go with this, there is now officially no way to call the real "log", unless we expose @GDScript directly (which isn't that bad an idea, actually). You can "vote" for it with :tada:
  • Call @GDScript::log -- if we do this, we can still call logger.gd::log using self.log or (if it is a singleton) Logger.log. You can "vote" for it with :heart:
  • Forbid func log -- if we do this, there is now no way to make a function called log (which would still be allowed in non-GDScript Scripts), unless we add func "log"() or func \log(), both of which are somewhat ugly. You can "vote" for it with :smile:

I would say this isn't about names or moving stuff somewhere. It's a question of how to deal with keywords in a language.

For my C++ bindings I had to forbid some function names (like new) and make them a valid identifier (in this case new_ because it could clash with GDScript._new()).

ClassDB (the structure/system that holds all class and method information) is completely language independent (and with that syntax independent).

GDScript itself is just a module, it's not part of Godot's core (even though from a user perspective it kinda is).

I personally don't think that the core names should be changed so something maybe less descriptive because GDScript has a keyword like that. I think GDScript should either implement context sensitive keywords (sync, master and slave are only ever valid in front of a function definition) or should document that methods that have the same name as keywords must be prefixed or anything.

I personally also think that the math functions shouldn't be keywords. Those functions shouldn't be part of the syntax, they should be part of the language's ecosystem.

Since more languages are coming anyway (C# for example), I suggest that GDScript simply deals with those names that could be keywords and documents that on the docs.

To make those special names clear in the docs I like the system a lot of Microsoft docs have:
there are tabs which show the function in the different languages (for example C#, F# and VB.NET). If the Godot docs could have a similar system then renaming problematic names wouldn't be such a big deal.

IIRC the C# binding has a lot of these exceptions because the language has so many keywords (correct me if I'm wrong @neikeq).

Just my 2 cents 馃槃

@karroffel In regards to C# reserved keywords I use @ as a prefix. e.g.: GDNativeClass.@new. There is no problem with sync, master or slave since those are attributes not keywords.

Perhaps we could add contextual keywords support to GDScript?

BTW, related/semi-dupe of #7321.

Noting down: String.match can't be called either

Was this page helpful?
0 / 5 - 0 ratings

Related issues

n-pigeon picture n-pigeon  路  3Comments

Spooner picture Spooner  路  3Comments

testman42 picture testman42  路  3Comments

EdwardAngeles picture EdwardAngeles  路  3Comments

mefihl picture mefihl  路  3Comments