Zinc

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Zinc相关的知识,希望对你有一定的参考价值。

ZINC 0.A.0.0

Table of contents

I. Why Zinc?

II. Because it is good for your brains

III. Enough propaganda, how do I use it?

IV. The small print

I. Why Zinc?

I've kept thinking that vJass' must remain as close to JASS as possible. This is a good thing as it keeps some consistency, but it might be bad as it forces it to JASS ideas that were not really good. vJass as the single language alternative was not going to work forever, so it has always been a plan to somehow let different languages in. Another reason for making a new language was the fun that is designing a language's syntax...

I only had the vague idea of making another language, until one day, I started trying to come up with it, and started to write random text in a wc3c.net pastebin thread, and asking some fellows for opinion. This event was inspired by some other things that were going on at that time that convinced me of this necessity. Basically, a lot of discussions about JASS' verbosity came to fruition, which caused me to really feel lame while re-coding my Hydra spell, suddenly I felt like typing more than I should. It was not just the verbosity, it was stuff like not making all members of a library implicitely private, or having to declare library private members before they are ever used. At that time there were alternatives to vJass that were very powerful, perhaps they were too powerful, and were full of things I was not looking for while not really taking real care of the problems I noticed besides of adding new problems. All was pointing towards it, it was the time to do it.

Zinc serves a purpose different to Jass, to be honest, it is main purpose is to test the grounds for more multi-language support, and to have fun implementing yet another language. But it should also be a good alternative to vJass in its own.

Zinc is actually an acronym that stands for ZINC is not C. However, to continue with JASS' tradition, you may call it Zinc or ZINC indistinctively.

Why call it it Zinc is not C? Well, because while the syntax tries to follow that classic C-style paradigm, it does not follow it 100%, there are parts of that style of syntax I didn't want to use. It also avoids giving the false impression that learning Zinc would somehow help you learn C, or that people that know C-style languages will have it easier with Zinc, and such non-sense...

Let me list the values, the rules behind its syntax and workings.

C-like syntax

So, the syntax is mostly inspired by C, why? Well, that‘s just because I like this syntax the most. In many ways, it is not really that great. It is not a panacea that will make your code more readable or anything like that, in fact, it has quite the potential to make your code incredibly hard to follow. Yet, I like it, it is for simple things things like {} and not having to use them for a single statement. (Using {} gives you a certain advantage when your editor has bracket highlighting)

Less verbosity

Many guys do not like verbosity, yet it is really one of the things that makes Jass (and vJass for that matter), much easier to follow. It has pros and cons, Zinc is meant as a complement to vJass. vJass fails at being quick to write, yet it is very readable for the most part. Zinc will sacrifice much of this readability to allow some code that is faster to write.

It is not just the large amount of keywords you have to write, it is also the amount of keywords you can write. Zinc aims to reduce both. Although you will still see things like library you will not see anything like array... Zinc tries to use operators where possible or recycle keywords.

This is again, no panacea, it is a trade off. Code will become more symbollic or context-requiring, and will need some extra attention and knowledge to read.

Readability

That Zinc does not likes verbosity should not be an excuse to make the language downright unreadable, this is one of the reasons Zinc does not blindly follow C-like syntax. It is also the reason you won't see silly abbreviations to merely cut a token's length.

Segregation

This is a very important thing, if we allowed vJass and Zinc code to get thrown together anywhere without distinction, a) Everything would get very hard to read, b) There would be no consistency and most importantly: c) it would be very hard to parse...

Zinc is a different language than Jass, and thus it should be kept in different files and 'triggers' than it. vJass syntax should give errors when used inside Zinc areas, and viceversa.

Structured programming

What really made me start this all over, was seeing the loop..exitwhen..endloop construct while coding Hydra. Zinc introduces while and loop, and kills that loop stuff. This makes it closer to a well structured code. Rather than spaguetty all around-

vJass interoperability

We should be able to use Zinc in a library even if all the remaining libraries in the map are in vJass. We should be able to call vJass libraries from Zinc and vice versa. Nuff said. Why? Because having to port whole code to other languages is work that we can't spare. Also because vJass is a good language by itself, and we cannot expect everyone to switch from it. In fact, they shouldn't, as there is a lot more documentation to vJass, and it has a more human readable syntax, it is a good language for many people.

Do not "live a lie"

It is best to avoid implementing features that look cool in paper but do not plain work when compiled.

Rigidity

If you used vJass and read the stuff in this page, you will soon notice Zinc has fewer features. For example, scopes are missing from the picture. There are lot of rules to Zinc that force you to do things more correctly. For example, you are pretty much forced to use libraries if you wish to use Zinc at all. You cannot even write a function without a library. What's more, all things inside a library are private - not public - by default.

Fix vJass mistakes

This and rigidity go together. There are many things about vJass that I ended not liking at all, yet removing/changing them would break backwards compatibility. I am taking the chance that Zinc is a new language to kill them.

II. Because it is good for your brains.

Zinc is good for your brain's development. Likewise, the Zinc scripting language is good for ...your code development. So let's see some examples of code...

Hello world

library HelloWorld
{
    function onInit()
    {
         BJDebugMsg("Hello World");
    }
}

For better or worse, Zinc is completely library-based. So we begin by declaring a Hello World library. Unlike vJass, there is no need to explicitly declare an initializer function name, and the function called onInit will be used directly (if it exists), The function has no arguments, and no return value. It simply calls a jass function BJDebugMsg to do the text display.

99 bottles of beer

    library Bottles99
    {
        /* 99 Bottles of beer sample,
           prints the lyrics at: http://99-bottles-of-beer.net/lyrics.html
        */
        function onInit()
        {
            string bot = "99 bottles";
            integer i=99;
            while (i>=0)
            {
                BJDebugMsg(bot+" of beer on the wall, "+bot+" of beer");
                i=i-1;
                if      (i== 1) bot = "1 bottle";
                else if (i== 0) bot = "No more bottles";
                else            bot = I2S(i)+" bottles";
                //Lazyness = "No more" is always capitalized.

                if(i>=0)
                {
                    BJDebugMsg("Take one down and pass it around, "+bot+" of beer on the wall.\n");
                }
            }
            BJDebugMsg("Go to the store and buy some more, 99 bottles of beer on the wall.");
        }
    }

This sample illustrates yet more syntax elements, you can see the while and if constructs, also how {} is actually optional when it is a single statement. Also some comments. There is no elseif construct in Zinc, because as you can see, nesting an if inside an else is just as easy.

Factorial

    library Factorial
    {
        /*
        *   Calculates the factorial of a number
        *
        */
        public function Factorial(integer n) -> integer
        {
            integer result=1, i;
            for ( 1 <= i <= n)
                result = result * i;

            return result;
        }
    }

This simple code is a function that calculates the factorial of a number n, that is 1*2*3*...n . Notice the for loop construct. It basically means that the code should be repeated for all numbers i between 1 and n. Also notice that two integer variables are declared in the same line. We probably wish to call this function in other libraries, so we declare the function as public.

Instant Kill

    library InstantKill
    {
        /*
        *   INSTANT K1LL !
        *   This custom spell Kills the target unit!
        *
        */
        constant integer SPELL_ID = ‘A001‘;

        function onSpellCast()
        {
            KillUnit(  GetSpellTargetUnit() );
        }

        function spellIdMatch() -> boolean
        {
            return (GetSpellAbilityId() == SPELL_ID);
        }

        function onInit()
        {
            trigger t = CreateTrigger();
            TriggerAddAction(t, function onSpellCast);
            TriggerAddCondition(t, Condition(function spellIdMatch) );
        }
    }

This is a quick custom spell that will kill the target unit instantly. You may notice the syntax for return values and also that constant global declaration not inside a globals block.

AddSpecialEffectTimed

    library TimedEffect requires TimerUtils
    {
        /*
        *   Adds a special effect to the ground, destroys it after
        *
        */
        struct data
        {
            effect fx;
        }
        function destroyEffect()
        {
            timer t=GetExpiredTimer();
            data dt = data( GetTimerData(t));
            DestroyEffect(dt.fx);
            ReleaseTimer(t);
        }

        public function AddSpecialEffectTimed( string fxpath, real x, real y, real duration)
        {
            timer t=NewTimer();
            data  dt = data.create();
            dt.fx = AddSpecialEffect(fxpath, x, y);

            SetTimerData(t, integer(dt));
            TimerStart(t, duration , false, function destroyEffect);
        }
    }

This is a library that comes with the AddSpecialEffectTimed, just an example. TimerUtils is a vJass library, but that is not a problem for Zinc. Do notice that all members of a library are private by default, which means that the data struct and the destroyEffect function are private. Public library members also work differently in Zinc than in vJass, it will not add a prefix to the name, therefore the way to call this function is AddSpecialEffectTimed, not TimedEffect_AddSpecialEffectTimed.

As you can see, things like type casts work the same as in vJass. There is also a struct which follows the same declaration rules as local variables and global variables.

CountUnitsInGroup

    library CountUnitsInGroup
    {
        integer count;
        public function CountUnitsInGroup( group g ) -> integer
        {
            count = 0;
            ForGroup(g, function() { count= count + 1; });
            return count;
        }
    }

This example replicates a blizzard.j function, as an excersise I invite you to check blizzard's implementation of it in Jass. This example features an anonymous function which are simply a way to allow you to declare functions on the go in cases like this one (very frequent in Jass) where the count=count+1 action is not really part of another function's logic but something inherent to the CountUnitsOne.

III. Enough propaganda, how do I use it?

zinc..endzinc preprocessor

Zinc and vJass code must be kept separate from each other. One way is using a vJass preprocessor command to tell it where zinc code begins and ends. I do not prefer this method over zinc import, but it should be good for people that can't adapt to working outside of WE (and therefore require newgen pack).

It is easy, if you wish to code in Zinc, you must first use a //! zinc preprocessor. However, you must also specify where the code ends, and thus add a //! endzinc preprocessor.

//! zinc

    library HelloWorld
    {
        function onInit()
        {
             BJDebugMsg("Hello World");
        }
    }

//! endzinc

I assume you got newgen pack installed (and jasshelper 0.9.Z.0 or greater), try to put this code in some "trigger", then test the map and see your first Zinc program at work.

import preprocessor

A saner way is this extension to the good, old and reliable import preprocessor in vJass. It is now [//! import [vjass/zinc] "filename"], which means that while importing the file you may also specify the language in the file. This language specification is optional and implied from the place in which you call it. In example, if you call it from a vJass area, it will assume the imported file is in vJass format unless you explicitely include the zinc word. Likewise, if you perform the import from a Zinc area, it will assume the imported file is in zinc language.

    //imports the file main.zn from code folder, consider it in zinc language
    //! import zinc "code/main.zn"

    //imports the file libTimerUtils.j from code/libs folder, consider it in vJassc language
    //! import vjass "code/libs/libTimerUtils.j"

    //imports the file spell1.j from code folder, use the current language.
    //! import "code/spell1.j"

Syntax basics

In Zinc, every statement/command will either start a block of code or finish with a single ; character.

; Means that you are ending what you are saying. It works to separate different statements of code. Other languages, like jass, would often need whitespace to make this differentation, for example, require a line break after each command.

Code blocks are groups of code inside two bracket delimiters, { marks the beginning of a code block, and } marks the end of a code block. All syntax constructs that require code blocks use this same syntax.

Since most things ignore whitespace, you can pretty much do plenty of things. You can have multiple declarations in the same line, and you can also split a declaration that is very long into multiple lines. Notice that this means that you will need some self control, and also setting up some code style standards for your code would not hurt.

There are two comment constructs: // and /* .. */. // Makes the compiler ignore all the text between // and the next line break. While /* */ can make it ignore all the text between the /* and the */. /* */ comments can be nested.

library

Libraries are the main, mandatory construction blocks in Zinc, they are defined as a group of code, variables, and data structures that do something for your own good. Libraries may use stuff from other libraries, in which case they need to explicitely say so by mentioning a "requires". Libraries are also your way into running code. If a library has a function called onInit, this function will get called on startup.

    library CodeGoesHereo
    {
        // code goes here:
           // * global variables
           // * functions
           // * structs
           // * interfaces
           // * other types (dynamic arrays, function pointer types)

        function onInit()
        {
            //Your onInit() function gets called when the map is starting.
        }
    }



If we wish to use stuff from library BB inside our library AA, we need to tell the compiler that the library AA, requires library BB. Basically, when your library requires other libraries, first the onInit functions of the other libraries get executed before yours. Their code is also moved to a place that is above yours in the output code.

    library AA
    {
        public function AABoom()
        {
            BJDebugMsg("AA BOOM");
        }
        function onInit()
        {
            BJDebugMsg("AA");
        }
    }

    library BB requires AA
    {
        function onInit()
        {
            BJDebugMsg("BB");
            AABoom();
            AABoom();
        }
    }



Do notice that a library may have many requirements, in that case, separate each requirement by a comma ( library name requires requirement1, requirement2, ... requirementN ).

This last sample illustrates a public library member:

requirements can be made optional by adding the optional keyword before the requirement's name, this makes the compiler just consider the requirement if the required library exists, without giving any syntax errors if the requirement is not found. This is useful when combined with static ifs.

library public or private

Library members (variables, functions, structs, interfaces, types) can be either private or public.

  • private: Only your library has access to the member in question, other libraries may have their own private or public members using the same name, it would not matter.
  • public: The member is available for use on other libraries. If two public such members in different libraries have the same name, an error will happen, therefore, please be careful, and name your public stuff carefully.

When declaring a library member, you can place it inside a public or private block to specify what access rule you want. Note that library members are private unless you specify otherwise.

    library PublicTest
    {
        public
        {
            integer PublicTest1;
            integer PublicTest2;
            integer PublicTest3;
            //all these variables are public.
        }
    }



If you are only including a single member in one of these blocks, the {} are optional. There is also no need to include all public members in the same block. This looks like vJass/Java/etc code, but is equivalent to the previous example:

    library PublicTest
    {
        public integer PublicTest1;
        public integer PublicTest2;
        public integer PublicTest3;
    }



Globals

Global variables, are those things that hold a state and any function can use. If these variables are public, every function outside the library will also be able to use. There is also the aspect of global constants, which behave exactly like global variables except they are read-only.

They are declared inside a library, and the syntax is: ( (constant) type varname ( = initialvalue) ). Adding the constant prefix makes the global a constant (which means it is read-only, in that case the initial value is mandatory, else it is optional. Another variation is when you want arrays. You can use name[] for an array with default size (8191), name[SIZE] for an array with another size value (which may be bigger, but if it is bigger than 8191, it will need function calls to be read), name[SIZE1][SIZE2] for a 2D array, the total storage needed for this array is SIZE1*SIZE2. Arrays cannot be assigned to an initial value.

You may also separate globals of the same type using a comma.

    library PublicTest
    {
        //the following are private constants:
        constant integer SPELL_ID = ‘A000‘;
        constant real    HEIGHT = 10.0;
        constant integer UNIT_COUNT = 7, ITEM_COUNT = UNIT_COUNT*6;
        
       // A, B, C, ... E are global variables of integer type,
       // they are public
        public integer A;
        public integer B = 1;
        public integer C,D = 1,E;

        // B and D begin assigned to 1
        real X[], Y[16000];
        real Z[200][200];
        //X: real array.
        //Y: real array of size 16000
        //Z: real 2D array 200x200.
        // They are private

        function onInit()
        {
            A = B;
            X[0] = A;
            X[1] = B+1;
            E = D;
            D = 7;
            C = E*3;
            Y[10000] = D;
            Z[4][4] = 10;
        }

    }



Functions

A function is a piece of code you can call, it may take some arguments and also have a return value. The syntax is : ( function name ([arguments]) (-> returntype { contents })

The arguments list is a comma separated list containing an item per argument, each argument is represented as the argument's type followed by its name.

If the function does not have a return type, then the -> is not included. If the function has a return type, then you must include a -> and the return type towards the end.

The contents of the function is a series of Zinc statements. It may include local variables, return statements, if-then-else, while, for, breaks, assignments, function calls and method calls.

To call a function, simply use functionname(argumentslist). The argument list when called is similar to the one when functions are declared, it is a comma separated list of expressions, one for each argument. If the function has a return value, you can use the function call in place of a value.

Notice that calling a function is... complicated. It requires the function to get declared in a required library or in the same library in a line above the line from which you are calling. Else there are alternatives, like the .evaluate() and .execute() methods which may get into detail later if I have time (they are act like in vJass‘)

There are Jass functions that come either from common.j or from blizzard.j that you may use following these rules. You may also use functions from vJass libraries that are required by yours.

    library FunctionTest
    {
        integer x = 0;
        function increaseX()
        {
            // a lame function that merely increases the
            // value of the x global
            x = x + 1;
        }

        function sum3(integer a, integer b, integer c) -> integer
        {
           //returns the sum of 3 numbers
           return a+b+c;
        }

        function onInit()
        {
            increaseX();
            x = sum3( x, x, 7);
            increaseX();
            /* BJDebugMsg = blizzard.j function  and I2S = wc3 native  */
            BJDebugMsg( I2S(x) );

            //should show a message containing "10" in game.
        }

    }



assignment

An assignment inside a function/method simply sets the value inside a variable, or array. It is syntax is (thing to assign) = (value). You may see many examples of assignments in the snippets above and bellow.

if

The syntax is : if (condition) { statements1 } else { statements2 } . If the condition is true, it will execute statements1, else it will execute statements2. Notice that the { } sorrounding the statements are only necessary if the statements are more than one. The else part is optional.

    library IfTest
    {
        integer x = 0;

        function onInit()
        {
            if(x==0)
            {
                BJDebugMsg("it is 0");
            }
            else
            {
                BJDebugMsg("it is not 0");
            }

            x = GetRandomInt(0,3);
            
            //shorter way (take advantage that the blocks are one liners
            // (does the same as above)
            if(x==0)  BJDebugMsg("it is 0");
            else      BJDebugMsg("it is not 0");

            x = GetRandomInt(0,6);
            if(x==5)
            {
                BJDebugMsg("Today is your lucky day, because you got a 5");
            }
            else if (x==0)
            {
                //There is no such thing as an elseif in Zinc, but as you
                // can see, since the only thing inside this else block is
                // an if statement, it can work the same.
                BJDebugMsg("Today is your unlucky day,")
                BJDebugMsg(" you got a 0");
            }
            else
            {
                BJDebugMsg("Normal day");
                //ifs can be nested.
                if( x==4) {
                    BJDebugMsg

以上是关于Zinc的主要内容,如果未能解决你的问题,请参考以下文章

(c)2006-2024 SYSTEM All Rights Reserved IT常识