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

Conditional GDScript parsing #6340

Closed
wkubiak opened this issue Aug 31, 2016 · 12 comments
Closed

Conditional GDScript parsing #6340

wkubiak opened this issue Aug 31, 2016 · 12 comments

Comments

@wkubiak
Copy link

wkubiak commented Aug 31, 2016

Operating system or device - Godot version:
Godot 2.1 stable

Issue description (what happened, and what was expected):
Like we have preprocessor statements in C or other languages where we can notify the compiler that such and such code should be compiled if a condition is true or not (like am I compiling on Windows or Linux for example), it would be nice to have such conditional parsing in GDScript.

Since 2.1 we have in GDScript the nice function "type_exists(string)", which returns a boolean value if a given type exists or not in our Godot instance.

The problem is, I can't use this tool for anything other than checking just that.

If I'd put that method call in an IF statement, the code under true/false conditions is still parsed by Godot and obviously this will result in an error because I write something regarding a non-existent type.

Thankfully Godot can just ignore the script file in which I write such statements, but still I have to make special arrangements just for that (like keeping all my platform-specific or module-specific code in separate scripts).

This is of course just a single example use-case.

Or maybe there is a way to force conditional parsing of which I'm not aware of? I asked around on the IRC channels but got no answers in this regards, so I suppose that it's not possible to do such a thing currently.

@neikeq
Copy link
Contributor

neikeq commented Aug 31, 2016

I don't like the C preprocessor. It's a completely different language on top of C. If we are going to add compile time code generation I would prefer to do it the D way: https://dlang.org/pretod.html

e.g:

func do_something():
    version Tools:
        # do something that requires a tools build
    else version Debug:
        # do something that requires a debug build
    else:
        # do whatever else

BTW, scripts are not compiled in debug mode (IINW). This means the preprocessor would need to be executed every time you run the project and that could be slow.

@wkubiak
Copy link
Author

wkubiak commented Aug 31, 2016

@neikeq I just used the C preprocessor as a comparison of functionality. It doesn't have to be done the exact same way. The "D way" of handling such situations looks nicer of course.

@SleepProgger
Copy link

SleepProgger commented Nov 24, 2016

While i'd love some Conditional GDScript parsing, i guess the mentioned problem could be solved if the ObjectTypeDB::instance function would be exposed to GDscript.
This way one could do:

if type_exists("SomeClass"):
  var some_instance = instance("SomeClass")

This would make it easy to use a module class if the module is available and fall back to a GDscript version if not.

For a dirty workaround to conditionally instance a class without the parser complaining if not present:

func __load_class(class_name):
	if ! type_exists(class_name):
		return false
	var _script = GDScript.new()
	_script.set_source_code("""
static func _load():
	return %s
		""" % class_name)
	if _script.reload() == 0: # ERROR.OK
		return _script._load()
	return false

Use like: __load_class("SomeRegisteredClass").new(arg1,...)

@vnen
Copy link
Member

vnen commented Nov 25, 2016

@SleepProgger you can already do that using the standard way of instancing (by calling .new()):

if type_exists("SomeClass"):
    var some_instance = SomeClass.new()

@SleepProgger
Copy link

@vnen You'd get a parser error if SomeClass does not exists.

@vnen
Copy link
Member

vnen commented Nov 25, 2016

@SleepProgger you are correct. I've seen this pattern applied before so I assumed it would work.

@bojidar-bg
Copy link
Contributor

bojidar-bg commented Feb 27, 2017

What about having (again D-way a bit) a parser-level const if? (+ intuitive expression not-constant errors)

func stuff():
  const if type_exists("SomeClass"): # if not, won't really parse contents, probably
    add_child(SomeClass.new())
  else: # No need of const here, but would be needed for elif I think
    show_unsupported_dialog("SomeClass")

@neikeq
Copy link
Contributor

neikeq commented Feb 27, 2017

static if to be more precise. Although @SleepProgger's problem could perhaps be solved by improving the parser to detect the type_exists condition.

Here is a dirty trick you can use to avoid this specific problem for now:

<edit>Ugh.. this was actually mentioned by the op. Any ways, I leave it if anyone is interested in an actual code example.</edit>

# someclass_factory.gd

static func get_new():
    return SomeClass.new()

# whatever.gd

var SomeClassFactory = null setget , get_someclass_factory

func get_someclass_factory():
    # must lazy initialize in order to work
    if SomeClassFactory == null:
        SomeClassFactory = load("someclass_factory.gd")
    return SomeClassFactory

func _ready():
    if type_exists("SomeClass"):
        var someclass_instance = SomeClassFactory.get_new()

From 3.0 there is a better and not hackish solution if all you want is to create a new instance (what else could you want? 😛):

func _ready():
    if ClassDB.can_instance("SomeClass"):
        var someclass_instance = ClassDB.instance("SomeClass")

@neikeq
Copy link
Contributor

neikeq commented Feb 27, 2017

To clarify, I am not sure if static if would not be a good idea. It would complicate static analysis (like code completion). I think a simple language like GDScript would do well just with the version keyword.

@RandomShaper
Copy link
Member

RandomShaper commented Mar 10, 2017

Without knowing this was being discussed, I opened another issue. I've closed it and I'm pasting here what I proposed:

Optimizing out OS.is_debug_build()

I tend to put assertions here and there in my GDScripts. Sometimes just not a plain assert but a call to a more complex routine to check if certain preconditions are met:

func _ready():
    if OS.is_debug_build():
        debug_check_nodes_z():

Do you think that letting the compiler handle OS.is_debug_build() as a constant could provide some benefit in terms of performance or something?

Optimizing out assert()

I've checked and the compiler still generates the opcodes although at runtime they are no-op in release builds.

Thus, wouldn't it be better to avoid even generating the opcodes?

UPDATE: I've submitted #8015 and #8016 for this (master/2.1)

@mhilbrunner
Copy link
Member

As there does not seem to be much interest in this and above improvements are now merged, as well as with 3.0 making some of this easier in other ways, vote for closing, @neikeq?

@wkubiak
Copy link
Author

wkubiak commented Nov 9, 2017

You are right. Issue seems to be irrelevant already because of the merged improvements.
Closing issue.

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

8 participants