Changes to the AGS Scripting language

Started by monkey0506, Thu 03/07/2014 21:58:10

Previous topic - Next topic

monkey0506

internal constructors would probably work for what I was proposing. I still think that static classes make sense as an indication that a type is only used to organize related data (methods and properties), all of which are static themselves, of course.

Calin Leafshade

I think we should also discuss the scoping of AGS script as currently its a bit archaic.

Could we feasibly add a two pass compiler and scope modifiers?

so we could have:

Code: ags


public class MyGlobalClass { };

class myPrivateClass {};

public function MyPublicFunc() { }

function myPrivateFunc() {}



So the local scope would be limited to the script but stuff can be exported with a simple modifier?

Calin Leafshade

I have started a standards document.

https://docs.google.com/document/d/1oW7dqB_GjbmlgPVoYzvBOyQevBPBjzc_jJPY2D5sDH8/edit?usp=sharing

Anyone can comment but if someone wants to do some writing then let me know your gmail address and i'll add you to the contributors.

I think a more formal process like this will allow us to do this quicker and more concisely.

I think discussion should essentially begin with some kind of proposed standard in the doc, followed by a discussion on that section on the forums, followed by edits, followed by consensus.

How does this sound to everyone?

Crimson Wizard

#43
First, may I ask of moving the related posts to another thread? It begins to be a separate discussion.

Second, I want to remind about what I said about implementing functions of builtin classes in script. I believe this should be denied. Class must have a modifier that would prevent game developer from "redefining" its member functions in script.

Third, there's one thing that bothers me. Please keep in mind, that AGS script is being used by newbie scripters who may have difficulties learning all those numerous keywords. Please, don't make AGS script require typing scope keyword before every function :(. Or think of compiler option that would allow to skip these.

Calin Leafshade

re: third point.

I think things would be assumed private first, like now.

What's simpler?

Code: ags


public function ThisIsGlobal() {}



or

Code: ags


//in header

import GlobalFunction;

// in script

function GlobalFunction() {}



The former is better because you can see the function is public immediately and its obvious how to make a function public.

the latter is bad because it's confusing and unclear.

My way is better for newbies i think.

Crimson Wizard

For some reason I thought you are talking about struct member functions.

Regarding global functions. I think you are confusing two concepts. Public/private is a concept of access. Import/export is a concept of location.
In AGS "import" does not mean "public" (in AGS everything is public by default). Its meaning is closer to C++ "extern": function/variable defined elsewhere.

Calin Leafshade

I will rephrase.

I think there should be a better way of accessing members from different scripts.

Ideally, imho, every member of a script should be scoped locally to that script (as is the case now) unless the member is prefixed by the modifier "public" or perhaps better "global".
If this is done then that member is accessible globally to all scripts.

Code: ags

//scriptA

int privateInt = 0;
global int PublicInt = 0;

//scriptB

function foo()
{
    Display("%d", privateInt); // COMPILE ERROR
    Display("%d", PublicInt); // compiles fine.
}

Crimson Wizard

#47
At the moment the sole purpose of script header is to make things accessible from other places. You may say that everything put in header is automatically public.
In such case, we either do not need headers anymore, or we do not need access modifiers for global functions/structs.

Calin Leafshade

Given the choice, I would remove headers.

They are a complex idea for newbies. Ideally we should move the responsibility of the header to the compiler, not the user.
I think the headers are a stop gap for the lack of a two-pass compiler.

monkey0506

I agree that the headers can cause more confusion than they might be worth. I've seen numerous beginners putting actual definitions in the header files, not understanding the import/export keywords. I find them perfectly easy to work with, and as CW said, comparable to C++'s extern, but if we can lower the learning curve for newbies then I'm all for it. Calin's suggestion for a global keyword seems reasonable to me, and it pretty clearly explains what it does.

I also understand we shouldn't add new keywords without consideration first, but I support this, and the two-pass compiler suggestion.

SpeechCenter

Removing the headers is a good idea, but what about existing code? Will it continue to function or require conversion?

As for keywords, I would try to stick to C# as much as possible, as long as it makes sense. So perhaps 'public' can still be used.
(another reason to restrict the number of new keywords is that it increases the possibility someone uses this keyword in existing code and then the code won't compile)

monkey0506

Another minor issue with managed user objects: If you try to create a struct B which has a protected or writeprotected member (field) of type struct A in the same script where struct A has been defined, then the compiler throws a syntax error that struct A is not a type.

Code: ags
// Script.ash
autoptr managed struct A
{
};

autoptr managed struct B
{
  A a1; // works fine
  protected A a2; // throws compile error
  writeprotected A a3; // throws compile error
};


This can currently be dealt with by simply moving the second struct definition to another script:

Code: ags
// ScriptA.ash
autoptr managed struct A
{
};

// ScriptB.ash
autoptr managed struct B
{
  protected A a; // works fine
};


Also, while I have your attention, might I ask how difficult it would be to allow dynamic arrays as struct members? We can already have pointer members to built-in and user defined types, so simply storing the pointer to the array seems as though it wouldn't be terribly difficult to manage...?

tzachs

Quote from: Crimson Wizard on Fri 04/07/2014 18:05:14
AFAIK in C# (if we take one for analogies) "internal" structs are those that cannot be used by external module. Here we have a struct that may be used externally (in scripts) but cannot be created with "new" by scripts.
Exactly, therefore Internal is not a good candidate in my opinion.

Quote from: SpeechCenter on Fri 04/07/2014 19:47:21
That would be confusing indeed. In C# static classes mean they cannot be instantiated (http://msdn.microsoft.com/en-us/library/79b3xss3.aspx). So either support that or define private/protected constructor.
But static implies you can have only one, this is not the case here, you do have instances.

Protected or internal constructors are an option but would not be friendly to the AGS scripters who has no access to the inners of the built-in types, and so will have to learn by compilation errors.

The "builtin" keyword might sound like a good option, but I think that having to invent a new keyword should ring an alarm that maybe we're doing something wrong here, maybe we're thinking we're dealing with a new beast that was not seen before, but that's not the case.
And I think the term that is missing here is interfaces.
Interfaces can be used but not instanciated. Replacing our built in types with built in interfaces (only in the API that's exposed to AGS script, the engine will use the same types that will just implement those interfaces) will give greater readability to the fact they can't be instanciated, and will also give some other extensibility benefits. A user will be able to provide a different implementation to the character interface, and it will work out of the box with modules that use the character interface, for example. And if we have sub interfaces like a position interface that is implemented by the character, object and gui interfaces, it will make the code much more flexible (the tween module could be much simplified, for example).

Snarky

Uh-oh! That's sounding even more complicated. Even though the logic is sound, let's try to avoid reinventing Java/C# if we can, eh?

I'd again like to repeat my proposal to not do anything, and just let people who try to construct new instances of the built-in types deal with the resulting errors.

Crimson Wizard

Yes, let's not overcomplicate things, I am sure there's a way to do this simply. :-\

Calin Leafshade

I think interfaces would not be semantically appropriate and referencing Characters and Objects directly rather than by proxy is more user-friendly.

I think we need to remember that this keyword will not be available to the scripter so it's kind of irrelevant in that sense.

Just pick a word and throw an error if the user tries to instantiate a class with that keyword. I don't think it need be any more complicated than that.

Also, the goal would be, at some point, to drop this restriction anyway and allow the user to instantiate them.. so it's not like its a permanent thing anyway.

Crimson Wizard

Quote from: Calin Leafshade on Mon 07/07/2014 17:27:29
Also, the goal would be, at some point, to drop this restriction anyway and allow the user to instantiate them.. so it's not like its a permanent thing anyway.
I agree with this.

monkey0506

Discovered another issue, though this might be one that we can reasonably write off. If a user struct only has imports, and does not actually define any members, then you cannot create a new instance of it ("requested user object of size 0"):

Code: ags
autoptr managed struct MyStruct
{
  readonly import attribute int MyProperty;
};

function game_start()
{
  MyStruct i = new MyStruct; // crashes
}


If there is a reasonable use case for instanciating a user struct like this (can't really think of one off the top of my head, the imports could all just be static) then it could be rectified by simply adding an unused char to the struct definition:

Code: ags
autoptr managed struct MyStruct
{
  protected char __unused; // $AUTOCOMPLETEIGNORE$
  readonly import attribute int MyProperty;
}

Crimson Wizard

Quote from: monkey_05_06 on Mon 07/07/2014 19:37:04
Discovered another issue, though this might be one that we can reasonably write off. If a user struct only has imports, and does not actually define any members, then you cannot create a new instance of it ("requested user object of size 0"):
Hmm... we may allow to create one. There's a real object created in any case (in engine), the user data is stored in internal buffer, which may be zero.

monkey0506

I'm looking into making the built-in instantiable types into autoptr types as a step toward deprecating the pseudo-pointer syntax. Sticking the keyword in front of the struct definitions was the easy bit, but now I have to figure out how to differentiate between the autoptrs and the actual instances that are being imported (like cEgo, which is a Character instance, not a Character*). I presume at this point I need to be looking in the engine code. Any pointers appreciated. (roll) Once I get it figured out I can submit a pull request for it.

SMF spam blocked by CleanTalk