I would like to introduce to you a programming language that I am working on named "Grape". It is a minimalistic programming language for StarCraft 2, and is not an extension to the Galaxy language. This means that there is no backwards compatibility to Galaxy; no need to make sure the language compiles in the Galaxy compiler as well.
It is indentation based, like Python and many other languages, and it encourages a simple, readable scripting-style of writing the code.
The parser, AST and code generation interfaces, along with the standard library, are finished. The only thing still needed is the Galaxy code generation.
Here are some examples of Grape code:
/**Grapestdlib-systempackage-containsbaseclassesforinteropwithSC2*Copyright(c)2011GrapeTeam.Allrightsreserved.**TheGrapeprogramminglanguageandstdlibarereleasedundertheBSDlicense.*/packagesystem.collectionsclasslistinheritsabstract_enumerable:staticintmaximum_items=2000privatestaticintdefault_capacity=4privatestaticobject[maximum_items]empty_arrayprivateobject[maximum_items]itemsprivateintsizeprivateintversionvoidadd(objectitem):ifsize>=maximum_items:thrownewmemory_limit_exceeded_exception("The amount of items in the list has exceeded the maximum amount of items a list can contain. If you need more memory available, raise the system.collections.list.maximum_items member.")size=size+1items[size]=itemversion=version+1voidadd_range(abstract_enumerablecollection):insert_range(size,collection)voidclear():ifsize>0:intindex=0whileindex<size:items[index]=nullindex=index+1size=0version=version+1boolcontains(objectitem):intindex=0whileindex<size:ifitems[index].equals(item):returntrueindex=index+1returnfalseintindex_of(objectitem):returnindex_of(item,0)intindex_of(objectitem,intstart_index):ifstart_index>size:thrownewargument_out_of_range_exception("start_index")returnindex_of(item,start_index,size-start_index)intindex_of(objectitem,intstart_index,intcount):ifstart_index>size:thrownewargument_out_of_range_exception("start_index")ifcount<0||start_index>size-count:thrownewargument_out_of_range_exception("count")intmax=start_index+countintindex=start_indexwhileindex<max:ifitems[index].equals(item):returnindexindex=index+1return-1voidinsert(intindex,objectitem):ifindex>size:thrownewargument_out_of_range_exception("index")items[index]=itemsize=size+1version=version+1voidinsert_range(intindex,abstract_enumerablecollection):ifindex>size:thrownewargument_out_of_range_exception("index")ifcollection==null:thrownewargument_null_exception("collection")abstract_enumeratorenum=collection.get_enumerator()whileenum.move_next():insert(index,enum.get_current())index=index+1version=version+1intlast_index_of(objectitem):returnlast_index_of(item,size-1,size)intlast_index_of(objectitem,intstart_index):ifstart_index>=size:thrownewargument_out_of_range_exception("start_index")returnlast_index_of(item,start_index,start_index+1)intlast_index_of(objectitem,intstart_index,intcount):ifsize==0:return-1ifstart_index<0||start_index>=size:thrownewargument_out_of_range_exception("start_index")ifcount<0||count>start_index+1:thrownewargument_out_of_range_exception("count")intmin=(start_index-count)+1intindex=start_indexwhileindex>=min:ifitems[index].equals(item):index=index-1return-1boolremove(objectitem):intindex=index_of(item)ifindex>=0:remove_at(index)returntruereturnfalsevoidremove_all():intindex=0whileindex<size:items[index]=nullindex=index+1size=size-1version=version+1voidremove_at(intindex):ifindex>=size:thrownewargument_out_of_range_exception("index")size=size-1items[index]=nullversion=version+1voidremove_range(intstart_index,intcount):ifstart_index<0:thrownewargument_out_of_range_exception("start_index")ifcount<0:thrownewargument_out_of_range_exception("count")ifsize-start_index<count:thrownewinvalid_operation_exception("")ifcount>0:size=size-countintindex=start_indexwhileindex<count:items[index]=nullindex=index+1version=version+1overrideabstract_enumeratorget_enumerator():returnnewlist.enumerator(this)objectget_item_at_index(intindex):ifindex>=size:thrownewargument_out_of_range_exception("index")returnitems[index]voidset_item_at_index(intindex,objectvalue):ifindex>=size:thrownewargument_out_of_range_exception("index")items[index]=valueversion=version+1intget_count():returnsizectorlist():passctorlist(abstract_enumerablecollection):ifcollection==null:thrownewargument_null_exception("collection")items=empty_arrayadd_range(collection)publicclassenumeratorinheritsabstract_enumerator:privatelistlprivateintindexprivateintversionprivateobjectcurrentvoiddispose():passoverrideboolmove_next():ifversion==l.version&&index<l.size:current=l.items[index]index=index+1returntruereturnmove_next_rare()privateboolmove_next_rare():ifversion!=l.version:thrownewinvalid_operation_exception("list.enumerator.version and list.enumerator.l.version do not match.")index=l.size+1current=nullreturnfalseoverrideobjectget_current():returncurrentoverridevoidreset():ifversion!=l.version:thrownewinvalid_operation_exception("list.enumerator.version and list.enumerator.l.version do not match.")index=0current=nullinternalctorenumerator(listl):this.l=lthis.index=0this.version=l.versionthis.current=null
Grape has full support in my modding IDE, Moonlite, including dynamic error checking, code completion, code insight, quick links, code navigation, refactoring, smart indentation and more.
I am currently looking for someone with good Galaxy knowledge who can write the code generation part (Just the implementation), because I have not done much modding.
I really like the syntax of grape, it looks very clean and is easy to read.
But I think exceptions are quite hard to compile to galaxy code as there are no exceptions and no gotos in galaxy. So you would basically have to check if an exception was thrown after every function call.
One way to fix this is to only allow check expressions, so you would just need to check for exceptions after calls to functions which can throw a exception. You could also look at the implementation of each function to see if it can throw exceptions or not.
An other problem with code generations are classes in general. There is not so much space available for arrays and I think arrays are the only possibility to implement objects. So your implementation of list alone would have an instance limit of 256 with no space left for other variables. Even without arrays in objects this limit can be hit quite fast.
So maybe you should add way to specify a maximum number of instances for each class. So the compiler could reserve less space for some classes and more space for often used classes.
Yeah, exceptions will be hard to implement, but I think it'll be possible. Since I can check whether a method contains throw statements and catch statements at compile time, I should be able to generate code based on that. I can then produce debug messages if an exception isn't caught in a parent method.
And yes, I had thought about the maximum number of iinstances for each class user specification (or rather, s3rius had) - it does indeed exist in Grape. This is how you specify a custom class size:
Will it be possible to have functions as types so that you can pass functions as parameter?
If 1. will be possible: Will it also be possible to define anonymous functions (lambda expressions in python) or nested functions?
What happens if you take an item out of the list and cast it to a wrong type? Will there be a dynamic type check or is it just the responsibility of the scripter to get the types right? Will it be possible to cast objects to ints so they can be stored in a units custom value?
In your example are some argument_null_exceptions. Maybe it would be better to not have null in the language. This would make all these checks needless. For cases where null is needed there could be a special type. For example it could look like this
unitz=null// compile errorunit?z=null// okunit?[10]units//question mark = this type is nullable//...unitu=units[3]//compile error, wrong typeunitu=units[3].Value// use "Value" to get the actual value, exception when value is nullifunits[3].HasValue:unitu=units[3].Value//now it is safe to get the valueunit?v=u// automatic cast to nullable typelista=newlist()list?b=newlist()// ....a.add(1)// cannot throw nullpointer exception because a cannot be nullb.add(2)// can throw nullpointer exceptionb?.add(3)// safe invoke, no exception is thrown if b is null, it just has no effect
This would make the language more complicated but a lot of functions would look nicer without null checks.
For the first versions of the language you could just include the syntax as a kind of annotation for the programmer and later add the type checks. At least for non primitive types this should be no problem.
Some more questions/suggestions:
Will it be possible to have functions as types so that you can pass functions as parameter?
If 1. will be possible: Will it also be possible to define anonymous functions (lambda expressions in python) or nested functions?
What happens if you take an item out of the list and cast it to a wrong type? Will there be a dynamic type check or is it just the responsibility of the scripter to get the types right? Will it be possible to cast objects to ints so they can be stored in a units custom value?
In your example are some argument_null_exceptions. Maybe it would be better to not have null in the language. This would make all these checks needless. For cases where null is needed there could be a special type. For example it could look like this
unitz=null// compile errorunit?z=null// okunit?[10]units//question mark = this type is nullable//...unitu=units[3]//compile error, wrong typeunitu=units[3].Value// use "Value" to get the actual value, exception when value is nullifunits[3].HasValue:unitu=units[3].Value//now it is safe to get the valueunit?v=u// automatic cast to nullable typelista=newlist()list?b=newlist()// ....a.add(1)// cannot throw nullpointer exception because a cannot be nullb.add(2)// can throw nullpointer exceptionb?.add(3)// safe invoke, no exception is thrown if b is null, it just has no effect
This would make the language more complicated but a lot of functions would look nicer without null checks. For the first versions of the language you could just include the syntax as a kind of annotation for the programmer and later add the type checks. At least for non primitive types this should be no problem.
Currently functions as parameters and anonymous functions will not be implemented. Maybe later, if I get the time. If you want, you can implement it; after all, the project is open source.
It is the responsibility of the scripter to cast correctly; if casted uncorrectly, the value will be null. Casting a class to int will be possible.
Nullable types will not be implemented and null will remain in the language.
The project is coming along, too - I just recovered my computer from a harddisk fail, and I am working on Grape once again. Working on getting the code generation to compile with the new AST, then I will implement the Grape package for Moonlite and then implement the code generation.
@peeeq: Go
If you want, you can implement it; after all, the project is open source.
Maybe I could help a little bit with the code generation or just by writing some automatic tests. Can I just checkout my code from http://code.google.com/p/grape-sc2/ and commit my changes there or how is this organized? Do I need any tools besides Visual Studio to build the project (I have not tried it yet)?
You only need Visual Studio (I believe it's made with VS2010, at least I can't remember that I had to run the compatibility agent when I opened it).
Since every revision is saved individually I guess there's no problem in uploading your own. Vestras has to add you in order for you to upload, however.
You are very welcome to work on the code generation, however the code generation library has to compile first, which is what I am working on.
As serius said, you only need Visual Studio 2010, however I recommend having IronPython Tools installed as well.
How will overriding and overloading methods work? Will it choose the method based on the runtime type or based on the compile time type?
I think using the dynamic type is the most intuitive solution but most languages I know use the static type.
Example with dynamic binding:
classA:stringfoo():return"A"classBinheritsA:stringfoo():return"B"classC:staticstringfoo(Aa):return"A"staticstringfoo(Bb);return"B";classTest:Aa=newB();stringx=a.foo()// x is now "B" ?stringy=C.foo(a)// y is now "B" ?
I also thought a little bit more of exceptions... What if you have a function which can throw an exception in a parameter. Take the following example where c is a function which can throw an exception.
ifa()andb(c(),d()):
So if c throws an expception d must not be executed because it comes after c. So to check for exceptions you would have to move c() in front of the if. But if you move c there it will always be executed, even if a() evaluates to false. To have it all correct this little if-statement would have to compile to something like this:
temp1=a();if(temp1){temp2=c();if(exceptionThrown()){return;// if this statement is not in a try- block we can just //exit the function, otherwise it would be even more complicated}temp1=a(temp2,d());}if(temp1){...}
I think it is quite tricky to get this transformation always right. Maybe the code generation should generate some kind of three address code as an intermediate step.
Resolving calls to overridden methods is always done virtually (otherwise there would be no point to overriding methods). Overloading however is usually resolved statically.
Rollback Post to RevisionRollBack
To post a comment, please login or register a new account.
Hello,
I would like to introduce to you a programming language that I am working on named "Grape". It is a minimalistic programming language for StarCraft 2, and is not an extension to the Galaxy language. This means that there is no backwards compatibility to Galaxy; no need to make sure the language compiles in the Galaxy compiler as well. It is indentation based, like Python and many other languages, and it encourages a simple, readable scripting-style of writing the code. The parser, AST and code generation interfaces, along with the standard library, are finished. The only thing still needed is the Galaxy code generation. Here are some examples of Grape code:
Grape has full support in my modding IDE, Moonlite, including dynamic error checking, code completion, code insight, quick links, code navigation, refactoring, smart indentation and more. I am currently looking for someone with good Galaxy knowledge who can write the code generation part (Just the implementation), because I have not done much modding.
Thank you for your attention.
I really like the syntax of grape, it looks very clean and is easy to read.
But I think exceptions are quite hard to compile to galaxy code as there are no exceptions and no gotos in galaxy. So you would basically have to check if an exception was thrown after every function call.
One way to fix this is to only allow check expressions, so you would just need to check for exceptions after calls to functions which can throw a exception. You could also look at the implementation of each function to see if it can throw exceptions or not.
An other problem with code generations are classes in general. There is not so much space available for arrays and I think arrays are the only possibility to implement objects. So your implementation of list alone would have an instance limit of 256 with no space left for other variables. Even without arrays in objects this limit can be hit quite fast.
So maybe you should add way to specify a maximum number of instances for each class. So the compiler could reserve less space for some classes and more space for often used classes.
@peeeq: Go
Yeah, exceptions will be hard to implement, but I think it'll be possible. Since I can check whether a method contains throw statements and catch statements at compile time, I should be able to generate code based on that. I can then produce debug messages if an exception isn't caught in a parent method.
And yes, I had thought about the maximum number of iinstances for each class user specification (or rather, s3rius had) - it does indeed exist in Grape. This is how you specify a custom class size:
Thanks for your interest.
Some more questions/suggestions:
See nullable types as in fantom: nullable types & safe invoke or nullable types in c#: http://msdn.microsoft.com/en-us/library/1t3y8s4s%28v=vs.80%29.aspx
This would make the language more complicated but a lot of functions would look nicer without null checks. For the first versions of the language you could just include the syntax as a kind of annotation for the programmer and later add the type checks. At least for non primitive types this should be no problem.
Nullable types seem like a poor solution for programmers who don't know how their own program flow works.
Is a simple if(x == null) that bad?
Currently functions as parameters and anonymous functions will not be implemented. Maybe later, if I get the time. If you want, you can implement it; after all, the project is open source. It is the responsibility of the scripter to cast correctly; if casted uncorrectly, the value will be null. Casting a class to int will be possible. Nullable types will not be implemented and null will remain in the language.
The project is coming along, too - I just recovered my computer from a harddisk fail, and I am working on Grape once again. Working on getting the code generation to compile with the new AST, then I will implement the Grape package for Moonlite and then implement the code generation.
Thanks for your interest and questions.
Maybe I could help a little bit with the code generation or just by writing some automatic tests. Can I just checkout my code from http://code.google.com/p/grape-sc2/ and commit my changes there or how is this organized? Do I need any tools besides Visual Studio to build the project (I have not tried it yet)?
@peeeq: Go
You only need Visual Studio (I believe it's made with VS2010, at least I can't remember that I had to run the compatibility agent when I opened it).
Since every revision is saved individually I guess there's no problem in uploading your own. Vestras has to add you in order for you to upload, however.
You are very welcome to work on the code generation, however the code generation library has to compile first, which is what I am working on. As serius said, you only need Visual Studio 2010, however I recommend having IronPython Tools installed as well.
How will overriding and overloading methods work? Will it choose the method based on the runtime type or based on the compile time type? I think using the dynamic type is the most intuitive solution but most languages I know use the static type.
Example with dynamic binding:
I also thought a little bit more of exceptions... What if you have a function which can throw an exception in a parameter. Take the following example where c is a function which can throw an exception.
So if c throws an expception d must not be executed because it comes after c. So to check for exceptions you would have to move c() in front of the if. But if you move c there it will always be executed, even if a() evaluates to false. To have it all correct this little if-statement would have to compile to something like this:
I think it is quite tricky to get this transformation always right. Maybe the code generation should generate some kind of three address code as an intermediate step.
Resolving calls to overridden methods is always done virtually (otherwise there would be no point to overriding methods). Overloading however is usually resolved statically.