Grape - SC2 programming language

Discussion in 'Starcraft 2 (SC2) Editor Help' started by Vestras, Dec 29, 2010.

  1. Vestras

    Vestras Retired

    Ratings:
    +249 / 0 / -0
    Hello,

    I would like to introduce you to a new project of mine, a new programming language for StarCraft 2.
    Before I start telling you about it, I would like to point out that everything here is subject to change and totally WIP. It is not ensured that this project will ever get released at this point.

    I'd like to start out saying that Grape was designed with one goal in mind: No Bullshit™.

    What does this mean? It means no trying-to-do-everything-at-once. It means no structs, no backwards compatibility, no properties/accessors, no fancy lambda syntax. Just clean, readable code that is as simple as possible:

    Code:
    package grape.sample
    
    import system
    import system.collections
    import system.utils
    
    // mod_base is a built-in class that provides all the base functions for a SC2 mod
    // for example, the user can override main which is run on map initialization
    class mod inherits mod_base
    	string args
    
        override void main(string args)
            string[100] args_array = args.split(" ")
    
            if args_array.null_or_empty
                return exit_code_failure
            end
    
            foreach string arg in args_array
                if arg.null_or_empty
                    break
                end
    
                game.echo(arg)
            end
    
    		// in grape, there are no for loops - I found while loops to be prettier, and more "scripting"-like
    		int index = 0
    		while index < 10
    			game.echo(index)
    			index = index + 1
    		end
    
    		bool debug_mode_on = false
    		bool is_unit_test = false
    		switch args_array[0]
    			case "debug_mode_on"
    				debug_mode_on = true
    			end
    			case "is_unit_test"
    				is_unit_test = true
    			end
    			default
    				// execute default action here
    				// this code is run when none of the above cases were handled
    			end
    		end
        end
    
    	void before_base_initialize()
    	end
    	
    	void after_base_initialize()
    	end
    
    	// Constructors/destructors
    	ctor mod()
    		init this("")
    	end
    
    	ctor mod(string args)
    		before_base_initialize()
    		init base()
    		after_base_initialize()
    
    		this.args = args
    	end
    
    	dctor mod()
    		args.destroy()
    	end
    end
    
    As you can see, this language is entirely "verbal" and line-based. The syntax is based partially on Ruby and partially on Lua.

    There are also some changes from normal language with OOP "logic". For example, all functions inherited from a base class may be overridden. The only case they may not be overridden, is if a function is declared as sealed. This means that there is no virtual modifier.

    This language is also very code completion friendly. So while it may take time to write without any "help", it will take almost no time to write in an IDE.

    Speaking of IDEs, this language will have full support in Moonlite, and no support in the StarCraft 2 Editor. In Moonlite, however, the features will be along the lines of full code completion like Visual Studio, Quick Links, navigation, etc., etc.

    At this point, I am totally open to suggestions for the syntax. However, I will not change the basics -- suggestions for a bracket language should not be posted and will be ignored.

    Here's a list of the features I have planned:
    • OOP: only classes, no enums, structs, interfaces -- only classes
    • all declared functions are overridable unless declared as sealed
    • full integration with Moonlite
    • the built-in Galaxy functions will not be usable except through the stdlib provided with the language.
    • "extensions" for the language will be possible, meaning C# code that processes certain items in the written code at compilation-time will be possible. This will open for a lot of things, such as preprocessing certain pieces of code and most likely debugging in Moonlite.

    Any feedback will be appreciated.

    PS: Yes, Moonlite is still going on. If you're wondering, v1.0.2.0 will feature a lot of new stuff - along with full Andromeda code completion and generally full Andromeda support. It will be released about the same time as Andromeda v2, most likely.
     
    • Like Like x 1
  2. Renendaru

    Renendaru (Evol)ution is nothing without love.

    Ratings:
    +309 / 0 / -0
    I like the idea behind this, it provides a quick debugging language, if I got the jist of it correctly, for testing things when you need something out there and coded without pure efficiency. (I only skimmed it very fast so..)
     
  3. Romek

    Romek Super Moderator Staff Member

    Ratings:
    +960 / 0 / -0
    The 'define' keyword seems a bit pointless.

    > no properties/accessors
    'Simple' is one thing, but some 'features' are almost necessary for coding. :p

    Other than that though, I like it. :thup:
     
  4. Vestras

    Vestras Retired

    Ratings:
    +249 / 0 / -0
    Define can be removed, if necessary.
    The reason I'm not planning properties is because I want to add stuff along the way. I might add it in like the second release version or something.

    :thup:
     
  5. Moogle

    Moogle New Member

    Ratings:
    +1 / 0 / -0
    "define var" is icky. You're using C syntax for function arguments (function main(string args)), and then you bring out some weird Visual Basic-like variable syntax for other stuff. Just keep it all C and we'll be much happier.

    Code:
    class mod inherits mod_base
        override int main(string args)
            string[] args_array = args.split(" ")
    
            if args_array.empty //Definitely keep that stuff to a minimum
                return exit_code_failure
            end
    
            foreach arg in args_array //Why declare the var type? The array has the type in it
                if arg.empty
                    return exit_code_failure
                end
    
                game.echo(arg)
            end
    
            //Kinda prefer ruby's block concept:
            args_array.for_each use arg
                  game.echo(arg) //Wouldn't say no to "game.echo arg" either!
            end
    
            // in a real world scenario, this function wouldn't return int
            // the reason for returning int now is to show how to return types
            return exit_code_success
        end
    end

    I love the lack of semicolons/required () around ifs.

    How is garbage collection going to work? You made a dynamic array.

    Can't say much, though I do support a nice language like this as an alternative to Andromeda. Andromeda is nice, but Galaxy is a scripting language and should be written as one.
     
  6. Gwypaas

    Gwypaas hook DoNothing MakeGUIUsersCrash -Vexorian

    Ratings:
    +50 / 0 / -0
    Just do what Vexorian did with vJASS then 95% of the people will be happy. Take Galaxy and extend it as far as possible and keep the gimmicky features that don't really fit into the Galaxy syntax at a minimum.

    We don't need a language that can't do anything because it won't help anything, nor do we need the shitty mess Andromeda is. We need something in between and that was what vJASS was for WC3.
     
  7. Vestras

    Vestras Retired

    Ratings:
    +249 / 0 / -0
    I haven't gotten as far as to garbage collections and stuff like that -- for now I'm only working on the parser.
    Due to public demand I have changed the syntax. Check out the new sample in the first post.

    I fully agree, and it is certainly what I intend to do with this.
     
  8. Im_On_56k

    Im_On_56k Hm...

    Ratings:
    +116 / 0 / -0
    Is grape going to be seedless? Looks like it at least. I do enjoy seedless grapes...eliminates downtime of removing those preposterous seeds that only get in the way of eating!

    That aside.

    Good luck on your project. :thup:
     
    • Like Like x 1
  9. Vestras

    Vestras Retired

    Ratings:
    +249 / 0 / -0
    Well of course, it will come without seeds so you can start enjoying Grape the instant you get it!
     
  10. Vestras

    Vestras Retired

    Ratings:
    +249 / 0 / -0
    Just a little announcement. I'm soon done with the parser - I only need to implement switch statements and the expression parser. Then I can start the code generator.
    I also updated the code sample.
     
  11. Vestras

    Vestras Retired

    Ratings:
    +249 / 0 / -0
    More code samples from the stdlib:

    enumerable_base.gp:
    Code:
    /*
     * Grape stdlib - system package - contains base classes for interop with SC2
     * Copyright (c) 2011 Grape Team. All rights reserved.
     * 
     * The Grape programming language and stdlib are released under the BSD license.
     */
    
    package system.collections
    
    // summary: Represents an enumerable collection of items.
    abstract class enumerable_base
    	// summary: Gets the enumerator_base of this enumerable_base.
    	abstract enumerator_base get_enumerator()
    	end
    end
    enumerator_base.gp:
    Code:
    /*
     * Grape stdlib - system package - contains base classes for interop with SC2
     * Copyright (c) 2011 Grape Team. All rights reserved.
     * 
     * The Grape programming language and stdlib are released under the BSD license.
     */
    
    package system.collections
    
    // summary: Supports a simple iteration over a generic collection.
    abstract class enumerator_base
    	// summary: Gets the current item in the enumerator_base.
    	abstract object get_current()
    	end
    
    	// summary: Moves to the next item in the enumerator_base.
    	abstract bool move_next()
    	end
    
    	// summary: Resets the enumerator_base.
    	abstract void reset()
    	end
    end
    list.gp:
    Code:
    /*
     * Grape stdlib - system package - contains base classes for interop with SC2
     * Copyright (c) 2011 Grape Team. All rights reserved.
     * 
     * The Grape programming language and stdlib are released under the BSD license.
     */
    
    package system.collections
    
    // summary: Represents a list of objects that can be accessed by index.
    class list inherits enumerable_base
    	static int maximum_items = 10000
    	private static object[maximum_items] empty_array = new object[0]
    
    	private object[maximum_items] items
    	private int size
    	private int version
    	
    	class enumerator inherits enumerator_base
    		private list l
    		private int index
    		private int version
    		private object current
    
    		override void dispose()
    		end
    
    		override bool move_next()
    			if version == l.version && index < list.size
    				current = l.items[index]
    				index = index + 1
    				return true
    			end
    
    			return move_next_rare()
    		end
    
    		private bool move_next_rare()
    			if version != l.version
    				throw new invalid_operation_exception("Unable to move_next. list.enumerator.version and list.enumerator.version.l.version are not equal.")
    			end
    
    			index = l.size + 1
    			current = null
    			return false
    		end
    
    		override object get_current()
    			return current
    		end
    
    		override void reset()
    			if version != l.version
    				throw new invalid_operation_exception("Unable to move_next. list.enumerator.version and list.enumerator.version.l.version are not equal.")
    			end
    
    			index = 0
    			current = null
    		end
    
    		internal ctor enumerator(list l)
    			this.l = l
    			index = 0
    			version = l.version
    			current = null
    		end
    	end
    
    	enumerator_base get_enumerator()
    		return new list.enumerator(this)
    	end
    
    	int get_count()
    		return size
    	end
    
    	int get_capacity()
    		return items.length
    	end
    
    	void set_capacity(int value)
    		if value != items.length
    			if value < size
    				throw new argument_out_of_range_exception("value")
    			end
    
    			if value > 0
    				object[value] dest_array = new object[value]
    				if size > 0
    					items.copy(0, dest_array, 0, size)
    				end
    
    				items = dest_array
    			else
    				items = empty_array
    			end
    		end
    	end
    
    	void add(object item)
    		if size == items.length
    			ensure_capacity(size + 1)
    		end
    
    		size = size + 1
    		items[size] = item
    		version = version + 1
    	end
    
    	void add_range(enumerable collection)
    		insert_range(size, collection)
    	end
    
    	void clear()
    		if size > 0
    			items.clear(0, size)
    		end
    
    		version = version + 1
    	end
    
    	bool contains(object item)
    		if item == null
    			int j = 0
    			while j < size
    				if items[j] == null
    					return true
    				end
    
    				j = j + 1
    			end
    
    			return false
    		end
    
    		int i = 0
    		while i < size
    			if item == items[j]
    				return true
    			end
    
    			i = i + 1
    		end
    
    		return false
    	end
    
    	private void ensure_capacity(int min)
    		if items.length < min
    			int capacity = items.length * 2
    			if items.length == 0
    				capacity = 4
    			end
    
    			if capacity < min
    				capacity = min
    			end
    
    			set_capacity(capacity)
    		end
    	end
    
    	int index_of(object item)
    		return items.index_of(item, 0, size)
    	end
    
    	int index_of(object item, int index)
    		if index > size
    			throw new argument_out_of_range_exception("index")
    		end
    
    		return items.index_of(item, index, size - index)
    	end
    
    	int index_of(object item, int index, int count)
    		if index > size
    			throw new argument_out_of_range_exception("index")
    		end
    
    		if count < 0 || index > size - count
    			throw new argument_out_of_range_exception("count")
    		end
    
    		return items.index_of(item, index, count)
    	end
    
    	void insert(int index, object item)
    		if index > size
    			throw new argument_out_of_range_exception("index")
    		end
    
    		if size == items.length
    			ensure_capacity(size + 1)
    		end
    
    		if index < size
    			items.copy(index, items, index + 1, size - index)
    		end
    
    		items[index] = item
    		size = size + 1
    		version = version + 1
    	end
    
    	int last_index_of(object item)
    		return last_index_of(item, size - 1, size)
    	end
    
    	int last_index_of(object item, int index)
    		if index >= size
    			throw argument_out_of_range_exception("index")
    		end
    
    		return last_index_of(item, index, index + 1)
    	end
    
    	int last_index_of(object item, int index, int count)
    		if size == 0
    			return -1
    		end
    
    		if index < 0 || index >= size
    			throw new argument_out_of_range_exception("index")
    		end
    
    		if count < 0 || count > index + 1
    			throw new argument_out_of_range_exception("count")
    		end
    
    		return items.last_index_of(item, index, count)
    	end
    
    	bool remove(object item)
    		int index = index_of(item)
    		if index >= 0
    			remove_at(index)
    			return true
    		end
    
    		return false
    	end
    
    	void remove_at(int index)
    		if index >= size
    			throw new argument_out_of_range_exception("index")
    		end
    		
    		size = size - 1
    		if index < size
    			items.copy(index + 1, items, index, size - index)
    		end
    
    		items[size] = null
    		version = version + 1
    	end
    
    	void remove_range(int index, int count)
    		if index < 0
    			throw new argument_out_of_range_exception("index")
    		end
    
    		if size - index < count
    			throw new argument_out_of_range_exception("count")
    		end
    
    		if count > 0
    			size = size - count
    			if index < size
    				items.copy(index + count, items, index, size - index)
    			end
    
    			items.clear(size, count)
    			version = version + 1
    		end
    	end
    
    	ctor list()
    		items = empty_array
    	end
    
    	ctor list(int capacity)
    		if capacity < 0
    			throw new argument_out_of_range_exception("capacity")
    		end
    
    		items = new object[capacity]
    	end
    end
    object.gp:
    Code:
    /*
     * Grape stdlib - system package - contains base classes for interop with SC2
     * Copyright (c) 2011 Grape Team. All rights reserved.
     * 
     * The Grape programming language and stdlib are released under the BSD license.
     */
    
    package system
    
    // summary: Represents the base class for all classes in grape - it is the ultimate grape base class.
    class object
    	// summary: Gets this object's unique hash code.
    	int get_hash_code()
    		return 0
    	end
    
    	// summary: Returns a value indicating whether this object instance is equal to the given object.
    	bool equals(object other)
    		return other == this
    	end
    
    	// summary: Returns a string that represents this object instance.
    	string to_string()
    		return "object"
    	end
    end
     
  12. phyrex1an

    phyrex1an Staff Member and irregular helper Staff Member

    Ratings:
    +446 / 0 / -0
    Releasing the stdlib under gpl2 seems like a bad idea. I'm not very knowledgeable about the sc2 map format but if it is anything like the wc3 one then using the stdlib in your map might force you to release the map under gpl2 too. A bsd style license might be better for the standard lib.
     
  13. Vestras

    Vestras Retired

    Ratings:
    +249 / 0 / -0
    Good suggestion, phyrex1an. Changed, and I updated the samples. The samples now parse correctly and a matching AST is generated.
     
  14. s3rius

    s3rius Linux is only free if your time is worthless.

    Ratings:
    +128 / 0 / -0
    I do like. Looks better than your first draft. And it's a good thing you decided to keep the return type in the signature where they belong :)

    Questshiunns:

    Members, by default, are public?

    How are you going to handle memory allocation? I see huge chunks of classes being created:
    private object[maximum_items] items;
    Since it seems array size doesn't have to be constant I guess you're making your own kind of allocation system that draws from huge chunks of normal memory?

    On that note I got some random idea. Since memory is very limited in Sc2 and since Grape's Galaxy code will probably eat a nice bit of it once you start using it excessively, maybe it's a nice idea to implement another keyword for variable declaration: table or extern or something like that. These variables would then be created in the Data Table instead of the usual way. It'd be a bit slower (did some benchmarking with data table read/write and it wasn't all that slow) but you could "externalize" larger arrays variables where speed doesn't matter as much.
    At least for native Galaxy var types this might be useful.

    We get accessors and/or operator overloading? Or is that already part of the gimmicky features mentioned above?
     
  15. Vestras

    Vestras Retired

    Ratings:
    +249 / 0 / -0
    Yes, members are public by default - kind of the same "pattern" as all members being overridable by default. I wanted everything to be open to the user.

    I have not yet come to the planning of code generation and allocation. I will have to look into Galaxy at that time, but I will first create the parser/syntax checker. One thing at a time ;)
    I like your extern suggestion, and it is definitely a possible. However, for now I would like to focus on actually having a release and having it work.

    We will not get accessors, as they don't fit into the language. Operator overloading is a possibility, but not in the first few versions.
     
  16. tooltiperror

    tooltiperror Super Moderator Staff Member

    Ratings:
    +233 / 0 / -0
    ctor and dctor are ugly names.
     
  17. Vestras

    Vestras Retired

    Ratings:
    +249 / 0 / -0
    I would like to announce that the parser is working properly, and that I have the error checker almost ready -- I only have expression error checking left.
    I will be getting help on code generation from serius, who is very good with galaxy, so the generated code should be as smooth and optimized as possible. When code generation is done, an alpha release will be ready.
     
  18. Gwypaas

    Gwypaas hook DoNothing MakeGUIUsersCrash -Vexorian

    Ratings:
    +50 / 0 / -0
    If you keep that syntax then I must sadly say this: Andromeda > Grape even with all the random stupid shit Andromeda has, it's still better than this.
     
  19. Vestras

    Vestras Retired

    Ratings:
    +249 / 0 / -0
    What part of it? Not that I'm intending to change it.
     
  20. tooltiperror

    tooltiperror Super Moderator Staff Member

    Ratings:
    +233 / 0 / -0
    All of it.

    Galaxy is a brace style language, why don't we keep it that way?
     

Share This Page