| 09-13-2009, 01:10 PM | #1 |
I'm currently in the process of programming a type-safe hopefully incremental compiler for a theoretical vJass replacement. I say "Theoretical" because I may very well give up in the face of vJass being not that bad, combined with lazyness. Anyway. This thread is to discuss my proposed syntax. My "Stage 1" plan for things to implement follows. Note that this is still all very much up in the air, and feedback is heavily desired: First, let me note that the new language will not be a superset of JASS. You will not be able to call normal JASS functions from within the new language. It will also not be compatible with vJass. This is not really negotiable, as it massively simplifies the compiler if normal JASS code, and especially vJass code are simply made illegal. It also opens the language to having a lot more choice in syntax, without having the pollution of the old syntax beside it. Secondly, it's still endline-sensitive. I love that about JASS, and I probably won't change my opinion on this matter unless you can bring forth a really cogent argument. Code will be separated in to compilation units. Each compilation unit must have a name. I dont know if I'll allow using the same name for multiple logical compilation units... I dont think I will. Example is worth more than words: JASS:namespace MySpellCode import SomeCompilationUnit import SomeOtherStuff import MainMapCode // Functions, variables, etc etc. "public" would no longer mean prefixing shit with the scope/library name and an underscore. It would mean the same as not specifying, with "private" staying basically the same. Like in Java, you can either import a namespace and have it's identifiers available locally, or use fully qualified names (eg: MyLibrary.MY_CONFIGURATION_VARIABLE) Circular dependencies would be allowed, with massive use of TriggerEvaluate. Hopefully I can make the compiler smart enough to always pick the optimal (fewest TriggerEvaluate calls) solution all the time. Order of initialization would be really straightforward, except in the case of circular dependencies. (Of course, your code should never depend on order of initialization when using circular dependencies...) For declaring functions, I haven't decided between: JASS:string MyFunc(integer i, string s) end JASS:function string MyFunc(integer i, string s) endfunction Input on that would be awesome. For declaring globals: JASS:namespace Example integer MyGlobalVariable = 42 constant integer MY_CONSTANT = 0 For inside functions: JASS:void Example() SomeLibrary.SomeFunction(42, 3, "abc") integer i = 12 loop exitwhen i == 3 i -= 1 end if i == 4 ExplodeUniverse() else Win() end end If functions keep "endfunction" instead of just "end", everything else will keep the long end keywords. Notice the variable declared in the middle of the function. It always annoyed me that you had to declare even the most throwaway values at the top of the function... |
| 09-13-2009, 01:30 PM | #2 |
Well earth fury, I'm going to suggest things that you don't want to hear. The ideal in my opinion is to use as much syntactic sugar as possible. I'd like newline and ; as EOL marker. That way the for loop syntax isn't a pollutant if/when you add it. I'd perfer functions to be declared within the scopes of a {}. i.e. JASS:void myFunc(int i) { //code goes here } As for declaring variables anywhere, that is all good. But I'd prefer that declaring variables must be available in the scope of brackets, but disallow shadowing as it never is a good idea. JASS:void myFunc(int i) { if(i==0) int someVarThatOnlyExistsInThisBlock = i; someVarThatOnlyExistsInThisBlock = i+1 //throws a compile error. } I've noticed you stripped the call/set/local/globals etc, and its a good thing you do, as they really server no valid reason. Other things include:: Function overloading by type & arity JASS:void SomeFunc(int i) void SomeFunc(bool b) void SomeFunc(int i,int j) I'd also like a runtime stack implementation. Here me out before you call me nuts:: Create a global stack that is inaccessible to the user. Just have it included whenever the map is compiled. { will push on a "start" symbol, while } will clear the stack, and call each item on the stack's deconstructor until a "start" symbol is found. There should be some sort of keyword to implying to declare the variable on the stack as opposed to on the heap. Hell even if it is STACK it's probably ok. Since non-native types & native types are going to be different your going to have push on a pointer that will hold type-information as well as the Id of the object. JASS:void myFunction(int i) { Location loc = STACK new Location(0,0) MyType myVar = STACK new myType(); // some code follows } JASS:Stack.Push(StartSymbol); Stack.Push(new NativePointer(new Location(0,0))); Stack.Push(new NonNativePointer(new myType()); That way you get RAII, while not-quiet garbage collecting can allow disciplined coders to avoid memory leaks. |
| 09-13-2009, 02:01 PM | #3 | |||||||
Quote:
That's why I started the topic ;) Quote:
Oddly, it would take about 2 seconds to implement ; as EOL in my current experimental tokenizer, so it's not something I really don't want to hear ;) Quote:
That's actually a possibility I'm considering. However, i would want it to be very limited compared to C/++/etc. Namely, { could only appear on a line by itself, or directly following a syntax construct (barring whitespace, of course.) Of course, } would have to be on it's own line. Always. And they would never be optional. Why? Because abuse of { and } are the biggest reason it hurts my brain to read a lot of C-style code. My desire to so limit {} is the main reason I'm heavily considering alternates... Note to some readers: Yes, I know you want to crucify me for even considering it to any extent. To you people, I say: Spend some time programming in a different language, you mooks. Quote:
I say "Fuck no" to shadowing. It is the root of all evil. Quote:
Those are the keywords that inspired me to say "fuck you" to vanilla JASS syntax completely. :) Quote:
That's off in the theoretical world of Stage 2 or 3 of implementation, but defiantly a priority. Quote:
I've been thinking about something like that, actually. However, I think that's a bit much to add directly in to the language, and the overhead of doing it all at runtime would be rather high... Instead, I think I'll just add RAII destruction to the eventual OOP objects, and not to native types. A standard library will be included. (As the useful shit in Blizzard.j becomes unusable.) I have no idea yet if the standard library would include wrappers like a Unit class. (Mainly because implementing OOP is a while away in the language design, let alone implementation) |
| 09-13-2009, 02:19 PM | #4 |
I'd say, that if you're building a whole new language, rather than an extension, you should really go with {} instead of function and endfunction. So defiantly JASS:string MyFunc(integer i, string s) { } |
| 09-13-2009, 02:32 PM | #5 |
I am a huge fan of saying { and } can only be on a new line. Runtime shouldn't really be bad for most uses. I'd like it as then you can basically use {} like a using keyword in C# once you add the RAII stuff. I really think it wouldn't be bad to include support for all agent types. There aren't that many, and you can use binary search on the enum of the type in your native pointer. To avoid doing a ton of worthless comparisons. JASS:void SomeFunc() { Location loc; for(int i = 0;i<10000;i++) loc = new Location(i,i); printf(I2S(GetHandleId(loc))); // I have no idea on what your print syntax should be out... } |
| 09-13-2009, 02:34 PM | #6 | ||
Quote:
Quote:
If you were thinking about adding support for 'for' and 'while' loops, I'd suggest against it. I think the regular loop/end that this will support is enough. However, it would be nice to have a 'break' keyword instead of 'exitwhen true', just for convenience. Your current syntax is nice. If I wanted C syntax in Jass, I'd just go after cJass I guess. But I much prefer this style. |
| 09-13-2009, 02:34 PM | #7 |
AI agree with MidWox, a C++/Java syntax would be nice. Also, I suggest the use of the keyword "protected" that would be used for heritage with variables. Though I don't know if starting this is a good idea, specially when sc2 is each day closer, even if you end your project, it is very unlikely that it will ever replace vJass. |
| 09-13-2009, 02:58 PM | #8 |
That is part of the fun of cJass is that if you really don't like syntax like exitwhen true you can go and write:: define break = exitwhen true Although you can't write a continue :/ |
| 09-13-2009, 03:12 PM | #9 | |||||||||||
Quote:
Depending on the amount of conversation this topic drums up, a public poll may be forthcoming... Quote:
Quote:
----------------- Quote:
yuuucckkkk..... Please post examples of usage of ; as EOL that you think would be useful! :) Quote:
Quote:
Quote:
I will add while() loops. for loops are a possibility. "loop" loops will always remain. "exitwhen true" may be named "exit", or "exitwhen" will be renamed "breakwhen" for parity. ---------------------- (We need a <hr> bbcode tag... or do we have one..?) Quote:
Defiantly. Also, it's "Inheritance". Quote:
---------------------------- Quote:
Of course, that doesn't stop people from making their own preprocessors for the new language... Which I will have to stab them for, because "define" is the root of IDEs sucking at dynamic syntax checking and such awesome stuff. Quote:
|
| 09-13-2009, 03:23 PM | #10 | |
Quote:
Something I've really been fond of lately is '?' and ':'. For example: JASS:void Example() /* ~ Code goes here ~ In the example you gave earlier, instead of this: if i == 4 ExplodeUniverse() else Win() end You could do this: */ (i == 4)? ExplodeUniverse() : Win() end |
| 09-13-2009, 03:32 PM | #11 | |
Quote:
... I want to force people to put shit on new lines for the sake of readability, and you're suggesting the most unreadable operator ever? xD... Also, it would be a whore to compile, except in an instance like that, which is generally considered abuse of the ternary operator. (Most style guides I like/agree with state the ternary operator is for variable initialization only.) |
| 09-13-2009, 03:55 PM | #12 |
I really like the ? operator, it helps alot with doing nullity checks. JASS:Match = new Match(sourceObj == null ? targetObj.Type : sourceObj.Type, sourceObject, targetObject) postincrement and preincrement: Correctly though! JASS:
bool Add(T obj)
{
bool retVal = false;
if(.m_Size < .m_Capacity) // range checking
this[.m_Size++]= obj;
retVal = true;
return retVal;
}
JASS:
bool Add(T obj)
{
bool retVal = false;
if(.m_Size < .m_Capacity) // range checking
int tempVar = .m_Size;
.m_Size = .m_Size+1;
this[tempVar]= obj;
retVal = true;
}
return retVal;
I'm all for readability. Anyway here's a putzing around method to determine type that isn't particularly smart, but I imagine it can't be too slow. It can be used to call the correct deconstructor of an agent type if and when you decide to implement RAII for agents:: Its written in cJass and I realize I violate my own rule but it's becuase Enums in cJass don't work right. It returns an integer for this method but that can easily be replaced. This thing isn't most optimal, as its all runtime. At compile time you should be able to determine type by reading constructor information and so you can speed this up even further by delineation of the if/else if tree, and switch it to a binary search. This thing should work although there are some problems as i was too lazy to remove parent types. It's a library so you can just go DT_DetermineType(...) JASS:library DT { private hashtable Table = InitHashtable(); public int DetermineType(agent a) int retVal = 0; if(a != null) { SaveAgentHandle(Table,0,0,a); if(LoadPlayerHandle(Table,0,0) != null)retVal = 1; elseif(LoadWidgetHandle(Table,0,0) != null)retVal = 2; elseif(LoadDestructableHandle(Table,0,0) != null)retVal = 3; elseif(LoadItemHandle(Table,0,0) != null)retVal = 4; elseif(LoadUnitHandle(Table,0,0) != null)retVal = 5; elseif(LoadAbilityHandle(Table,0,0) != null)retVal = 6; elseif(LoadTimerHandle(Table,0,0) != null)retVal = 7; elseif(LoadTriggerHandle(Table,0,0) != null)retVal = 8; elseif(LoadTriggerConditionHandle(Table,0,0) != null)retVal = 9; elseif(LoadTriggerActionHandle(Table,0,0) != null)retVal = 10; elseif(LoadTriggerEventHandle(Table,0,0) != null)retVal = 11; elseif(LoadForceHandle(Table,0,0) != null)retVal = 12; elseif(LoadGroupHandle(Table,0,0) != null)retVal = 13; elseif(LoadLocationHandle(Table,0,0) != null)retVal = 14; elseif(LoadRectHandle(Table,0,0) != null)retVal = 15; elseif(LoadBooleanExprHandle(Table,0,0) != null)retVal = 16; elseif(LoadSoundHandle(Table,0,0) != null)retVal = 17; elseif(LoadEffectHandle(Table,0,0) != null)retVal = 18; elseif(LoadUnitPoolHandle(Table,0,0) != null)retVal = 19; elseif(LoadItemPoolHandle(Table,0,0) != null)retVal = 20; elseif(LoadQuestHandle(Table,0,0) != null)retVal = 21; elseif(LoadQuestItemHandle(Table,0,0) != null)retVal = 22; elseif(LoadDefeatConditionHandle(Table,0,0) != null)retVal = 23; elseif(LoadTimerDialogHandle(Table,0,0) != null)retVal = 24; elseif(LoadLeaderboardHandle(Table,0,0) != null)retVal = 25; elseif(LoadMultiboardHandle(Table,0,0) != null)retVal = 26; elseif(LoadMultiboardItemHandle(Table,0,0) != null)retVal = 27; elseif(LoadTrackableHandle(Table,0,0) != null)retVal = 28; elseif(LoadDialogHandle(Table,0,0) != null)retVal = 29; elseif(LoadButtonHandle(Table,0,0) != null)retVal = 30; elseif(LoadTextTagHandle(Table,0,0) != null)retVal = 31; elseif(LoadLightningHandle(Table,0,0) != null)retVal = 32; elseif(LoadImageHandle(Table,0,0) != null)retVal = 33; elseif(LoadUbersplatHandle(Table,0,0) != null)retVal = 34; elseif(LoadRegionHandle(Table,0,0) != null)retVal = 34; elseif(LoadFogStateHandle(Table,0,0) != null)retVal = 35; elseif(LoadFogModifierHandle(Table,0,0) != null)retVal = 36; elseif(LoadHashtableHandle(Table,0,0) != null)retVal = 37; } return retVal; } |
| 09-13-2009, 04:28 PM | #13 |
{} is non-sense if you are going to limit it like that. Code:
whatever... end Code:
whatever...
{
}If you like surrealism you could use a single character I guess Code:
whatever... ; |
| 09-13-2009, 05:17 PM | #14 | ||||
Quote:
You, sir, win. Of course, I still won't implement it unless I find a simple way to do it sanely... Could always be done with a function call, actually. Though I'm still hesitant to add something which can so easily rape the whole language in it's tender ass. Quote:
++ and -- are a slight possibility. Depends how easy I will find it to do the code generation for them. I see no reason not to add them, barring implementation complexity for adding them correctly. +=, -=, etc. will all be added, of course. I dont think set operations will be allowed in any kind of expression. (of course in for() loops, but that's a bit of a special case...) Quote:
The shear theoretical overhead of that makes me scared to even think about how I would apply it... ------------------------------ Quote:
---------------------------- I think I've decided rather firmly on a choice between 3 basic syntax styles: JASS:// returning void would look like this probably: function Func() // May still do: function integer Func(), problem is that reads like shit. function Func():integer loop //Stuffff endloop return 1 endfunction // Versus void Func() Global += 1 end // Versus // ; is required to prevent stupidity like JavaScript. Full C-style syntax. void Func() { Global += 1; } My biggest complaint about C-style syntax is that JASS really doesn't benefit much from the braces... However, I'm doing proper variable scoping and would allow arbitrary scopes with {}, so it will gain some of the benefits... Also, C-style syntax is harder to parse... As for the first: I don't hate it, but it seems a bit excessive... However, the middle option seems not quite wordy enough if it's going to be wordy. Further input is needed... Most importantly: Which, if any of the three choices would make you NOT use the language? or, more gently, which if any would make you much more resistant to using the language? |
| 09-13-2009, 05:38 PM | #15 |
I suggest using indentation-based approach for scopes, just like Python does. |
