[WIP Tool] Indentation based vJASS syntax

Deleted member 219079

D

Deleted member 219079

I ditched programming for a long time. I did try numerous languages occasionally, including big ones Java and C++. To me it seems like those languages seek for the stars and forget simplicity. Maybe if I worked in a group project / it was my job, I could do C++. I just came across C couple weeks ago and I love its simplicity.

And what better place to return to coding than the ever so lovable hive?

I made this little tool that converts intendation based vJASS into normal vJASS, without compromising readability.


JASS:
library ayy requires optional SpellEvent

    //this is globals block
    globals
        private constant integer spellId='A000'

    private struct Ayy extends array
   
        static method onCast takes nothing returns nothing
            //its the spell!
   
        static if(LIBRARY_SpellEvent)
       
            //init with spellevent
            static method onInit takes nothing returns nothing
                call SpellEvent.create(spellId,function thistype.onCast)
       
        else
       
            static method onAnyCast takes nothing returns boolean
                if(GetSpellAbilityId()==spellId)then
                    call onCast()
                return false
       
            //init without spellevent
            static method onInit takes nothing returns nothing
                local trigger t=CreateTrigger()
                call TriggerRegisterAnyUnitEventBJ(t,EVENT_PLAYER_UNIT_SPELL_EFFECT)
                call TriggerAddCondition(t,Condition(function thistype.onAnyCast))
                set t=null
Converts to:
JASS:
library ayy requires optional SpellEvent

    //this is globals block
    globals
        private constant integer spellId='A000'
    endglobals

    private struct Ayy extends array
   
        static method onCast takes nothing returns nothing
            //its the spell!
        endmethod
   
        static if(LIBRARY_SpellEvent)
       
            //init with spellevent
            static method onInit takes nothing returns nothing
                call SpellEvent.create(spellId,function thistype.onCast)
            endmethod
       
        else
       
            static method onAnyCast takes nothing returns boolean
                if(GetSpellAbilityId()==spellId)then
                    call onCast()
                endif
                return false
            endmethod
       
            //init without spellevent
            static method onInit takes nothing returns nothing
                local trigger t=CreateTrigger()
                call TriggerRegisterAnyUnitEventBJ(t,EVENT_PLAYER_UNIT_SPELL_EFFECT)
                call TriggerAddCondition(t,Condition(function thistype.onAnyCast))
                set t=null
            endmethod
   
        endif

    endstruct

endlibrary


---------------

HOW TO USE

Download newest version from my post on this thread. Have your vjassindent's folder appended in your PATH, or do mklink "path to jassnewgenpack\vjassindent.exe" "path to vjassindent.exe" on an elevated command prompt, or manually replace the jassnewgenpack's vjassindent on new version.

Command line setup guide:
vjassindent --help gives you up-to-date argumentation guide.


Sublime Text setup guide:
First get this (by Ruke): http://www.hiveworkshop.com/threads/sublimetext-jass.240219/

1. Go to Preferences>Settings - User
2. Add to list: "trim_automatic_white_space": false
3. Go to Tools>Build System>New Build System...
4. Insert (Angle bracketed items need to be edited):
{
"cmd": "vjassindent \"$file_path\*.j\" -o \"$file_path\\out.j\" && copy \"<path to map.w3x>\" \"<path to map (out).w3x>\" && \"<path to jasshelper.exe>\" \"<path to common.j>\" \"<path to Blizzard.j>\" \"<path to map (out).w3x>\" && del \"$file_path\\out.j\"",
"working_dir" : "<path to jassnewgenpack>",
"selector" : "source.jass",
"quiet" : true,
"shell": true
}

Either have discrete folder for your indentation syntaxed files, or have different extension for them, for example .jx (and change build file's selection from *.j to *.jx)

Inside your map have //! import "path from jass folder to your out.j", jass folder being path to jassnewgenpack\jass
Troubleshooting:
General: Indentation based syntax means that the tool relies on your indentation to be able to parse your code, which makes the tool very strict on your indentation. Therefore always make sure your text editor doesn't trim white space! For problems about code CnP:d from internet giving you errors, use --fix-by-ceil.
Sublime Text: Make sure you actually have your sublime-build selected from Tools>Build System ("automatic" should work as well if you have Ruke's plugin installed). Then make sure you know how to compile from cmd, as the "cmd" value in .sublime-build is simply just a command written to cmd that's cd'd to "working_dir". After that check for correct formatting in your build file, quotes have to be \" and backslashes have to be \\.​

---------------

I'm way more ambitious than this (you have to start small :) ). I seek to create an entire vJass enhancement suite, including some new additions like generics, something like:

JASS:
struct LinkedList<Type> extends array
    readonly <Type> data
    readonly integer n
    readonly integer p
    //...
endstruct


I need help with this:
If I knew how jasshelper opens the .w3x file, reads its script and writes it over, I'd do my own vJASS compiler, instead of fiddling with some command line stuff.
Also if here's people who know C, I'd appreciate to go over the script of the tool (in my most recent post on this thread) and give me feedback on how it looks.


---------------

Edit: Updated setup guide
 
Last edited by a moderator:

Deleted member 219079

D

Deleted member 219079

Code:
//    vjassindent v1.0.0
//    by jondrean

// date: 20.06.16
// updated: 22.06.16

// ---------------

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

// ---------------

#define TAB_SIZE    4        //this many spaces is tab
#define MAX_INDENT     10      //this many tabs is max
#define BUFF_START    4096    //how many bytes prepared for file reading?

// ---------------

static inline int parse(char *in,long l,const char *fname);
static inline void clear(char **in,unsigned long *l);

// ---------------

int badlines=0;    //=has delimited comments
int nocomments=0,fix=0,replace=0 ;
char *out;
unsigned long outL;
unsigned long outAt;
char *tabs[MAX_INDENT];

// ---------------

int main(int argc,char **argv)
{
    FILE *tmp     = NULL;
    char *buff     = malloc(sizeof(char)*BUFF_START);
    long buffL    = BUFF_START;
   
    //give help
    if(argc==1 || (argc==2&&!strcmp(argv[1],"--help")))
    {
        printf(
            "Usage: vjassindent file1.j ... -o output.j\n"
            "\t--fix-by-floor,\n"
            "\t--fix-by-ceil   \teliminate bad indentation\n"
            "\t--help          \tshow this text\n"
            "\t--omit-comments \tdon't output comments\n"
            "\t--replace       \tinput file will be output\n");
        if(argc==1)system("pause");
        return 0;
    }
   
    //find output file's path
    for(int i=1;i<argc;++i)
    {
        if(!strcmp(argv[i],"-o"))
        {
            ++i;
            if(i==argc)
            {
                printf("vjassindent: expected file path after \"-o\"");
                return 1;
            }
           
            tmp = fopen(argv[i],"w");
            if(tmp==NULL)
            {
                printf("vjassindent: unable to create output file\n");
                return 1;
            }
           
            argc-=2;
            for(int j=i-1;j<argc;++j) argv[j]=argv[j+2];
            i-=2;
        }
        else if(!strcmp(argv[i],"--omit-comments")) nocomments=1;
        else if(strlen(argv[i])>9&&!memcmp(argv[i],"--fix-by-",9))
        {
            if(!strcmp(&(argv[i])[9],"ceil"))         fix = 2;
            else if(!strcmp(&(argv[i])[9],"floor")) fix = 1;
        }
        else if(!strcmp(argv[i],"--replace")) replace=1;
        else continue;
       
    }
       
    //give help (didn't specify output file's path)
    if((replace)?tmp!=NULL:tmp==NULL)
    {
        printf("vjassindent: bad output\n");
        return 1;
    }
   
    //get buffer
    outL     = BUFF_START;
    out     = malloc(sizeof(char)*(outL+1));
    if(out==NULL)
    {
        printf("vjassindent: unable to allocate buffer\n");
        return 1;
    }
   
    //form tabs
    tabs[MAX_INDENT-1]=malloc(sizeof(char)*MAX_INDENT);
    for(int i=MAX_INDENT;--i;)
    {
        tabs[MAX_INDENT-1][i]='\t';
        tabs[MAX_INDENT-i]=&(tabs[MAX_INDENT-1])[i];
    }
    tabs[0]=&tabs[MAX_INDENT-1][0];
   
    //go through given files
    for(int i=1;i<argc;++i)
    {
        //skip flags
        if(!memcmp(argv[i],"--",2)) continue;
           
        FILE *f=fopen(argv[i],"r");
        if(f==NULL)
        {
            printf("vjassindent: unable to open %s\n",argv[i]);
            return 1;
        }
       
        //get file length
        fseek(f,0,SEEK_END);
        unsigned long l=ftell(f);
        fseek(f,0,SEEK_SET);
       
        //make sure output buffer can take it
        while(l*3/2>outL-outAt)
        {
            if((out=realloc(out,sizeof(char)*(outL*=2+1)))==NULL)
            {
                printf("vjassindent: unable to reallocate write buffer, l:%lu, buff:%lu\n",l,((replace)?outL:outL-outAt));
                return 1;
            }
        }
       
        //make sure read buffer can take it
        if(buffL<l)
        {
            buffL=l;
            buff=realloc(buff,sizeof(char)*buffL);
            if(buff==NULL)
            {
                printf("vjassindent: unable to reallocate read buffer\n");
                return 1;
            }
        }
       
        //read file and clear
        l=fread(buff,sizeof(char),l,f);
        buff[l]='\0';
        clear(&buff,&l);
       
        //do parse
        if(!parse(buff,l,argv[i])) return 1;
       
        //write to file (if --replace)
        if(replace)
        {
            f=freopen(argv[i],"w",f);
            if(!fwrite(out,outAt-2,sizeof(char),f))
            {
                printf("vjassindent: failed to write to output file\n");
                return 1;
            }
            outAt=0;
        }
        fclose(f);
    }
    if(!replace)
    {
        if(!fwrite(out,outAt-2,sizeof(char),tmp))
        {
            printf("vjassindent: failed to write to output file\n");
            return 1;
        }
        fclose(tmp);
    }
   
    //return success
    free(buff);
    free(out);
    return 0;
}

// ---------------

const char *keys[9]        = {"endstruct\n","endlibrary\n","endscope\n","endfunction\n","endglobals\n","endmethod\n","endloop\n","endif\n","endmodule\n"};
const unsigned keyL[9]    = {10,11,9,12,11,10,8,6,10};

int emptys[MAX_INDENT]        = {0};    //how many empty lines after end<ele>?
char emptysLock[MAX_INDENT]    = {1};    //no more counting empty lines?

int queueM=-1;
unsigned char queue[MAX_INDENT];    //what key each indentation wants?

// ---------------

static inline void parse_line(const char *in,const long l);    // returns the index it left at
static inline int line_type(const char *in,const long l);    // 1=real line; 0=has comments; 2=empty line
int checknzero(char *string, int length);

// ---------------

//with this, we can postpone "endif" when necessary
#define IF_INDEX 7

//this is used in "parse"
#define QUEUE_READ(i) queue[i]

//these are used in "parse_line"
#define QUEUE_PUSH(i) {queue[++queueM]=i;emptysLock[queueM]=0;}
#define ITE_CMP(to,len) (l>len && !memcmp(&in[1],to,len))
#define ITE_BLOCK(to,len,i) if(ITE_CMP(to,len)) QUEUE_PUSH(i)

// ---------------

int dNow=0; //depth
static inline int parse(char *in,const long l,const char *fname)
{
    char *newlinepos;
    unsigned long index=0;    //reader pos
    unsigned lineL;
    unsigned curL=0;        //for error reporting
   
    int tempp=0;
   
    //go through given string "in"
    if(index!=l)
    {
        get_line:
        ++curL;
        newlinepos             = memchr(&in[index],'\n',l-index);
        lineL                 = (newlinepos==NULL) ? l-index : newlinepos-&in[index];
       
        //get whitespace, get indentation
        dNow=0;
        unsigned whiteL=0;    //depth, in bytes
        while(in[index+whiteL]<=32&&whiteL!=lineL)
        {
            if(in[index+whiteL]!='\t') ++dNow;
            else dNow+=TAB_SIZE;
            ++whiteL;
        }
        lineL-=whiteL;
       
        if(dNow%TAB_SIZE)
        {
            char err[lineL];
            switch(fix)
            {
                case 1://floor
                    dNow=dNow/TAB_SIZE*TAB_SIZE;
                    break;
                case 2://ceil
                    dNow=dNow/TAB_SIZE*TAB_SIZE+TAB_SIZE;
                    break;
                default:
                    memcpy(err,&in[index+whiteL],lineL);
                    printf("vjassindent: unable to parse %s, bad indentation:\n line %s%u: %s\n",fname,(badlines)?"~":"",curL,err);
                    return 0;
            }
        }
       
        //what kind of line?
        dNow/=TAB_SIZE;
               
        //this is where we actually append the "end<ele>:s"
        while(queueM>=dNow)
        {
            //postpone "endif"
            if(        queueM==dNow
                &&    QUEUE_READ(queueM)==IF_INDEX
                &&    lineL>=4&&!memcmp(&in[index+whiteL],"else",4))
                break;
           
            //append empty lines
            while(emptys[queueM])
            {
                memcpy(&out[outAt],tabs[queueM],queueM);
                outAt         += queueM;
                out[outAt++] = '\n';
                --emptys[queueM];
            }
           
            //append indentation
            memcpy(&out[outAt],tabs[queueM],queueM);
            outAt         += queueM;
           
            //append "end<ele>"
            memcpy(&out[outAt],keys[QUEUE_READ(queueM)],keyL[QUEUE_READ(queueM)]);
            outAt+=keyL[QUEUE_READ(queueM)];
           
            --queueM;
        }       
        switch(line_type(&in[index+whiteL],lineL))
        {
            case 2://empty line
                if(!nocomments&&queueM!=-1&&!emptysLock[queueM]) ++emptys[queueM];
                break;
               
            case 0://has comments
                if(nocomments)
                {
                    index=newlinepos-in+1;
                    if(newlinepos!=NULL)goto get_line;
                    else goto final_append;
                }
                break;
               
            case 1://ordinary line
                if(queueM!=-1)emptysLock[queueM]=1;
                if(dNow>MAX_INDENT)
                {
                    char err[lineL];
                    memcpy(err,&in[index+whiteL],lineL);
                    printf("vjassindent: unable to parse %s, too big indentation:\n\tlx: %s\n",fname,err);
                    return 0;
                }
               
                //look for keywords
                parse_line(&in[index+whiteL],lineL);
        }
                   
        //append indentation
        memcpy(&out[outAt],tabs[dNow],dNow);
        outAt         += dNow;
       
        //append current line
        memcpy(&out[outAt],&in[index+whiteL],lineL);
        outAt         += lineL;
        out[outAt++] = '\n';
               
        index=newlinepos-in+1;
        if(newlinepos!=NULL)goto get_line;
    }
   
    //append any remaining "end<ele>:s"
    final_append:
    while(queueM>=0)
    {
        //append empty lines
        while(emptys[queueM])
        {
            memcpy(&out[outAt],tabs[queueM],queueM);
            outAt         += queueM;
            out[outAt++] = '\n';
            --emptys[queueM];
        }
       
        //append indentation
        memcpy(&out[outAt],tabs[queueM],queueM);
        outAt         += queueM;
       
        //append "end<ele>"
        memcpy(&out[outAt],keys[QUEUE_READ(queueM)],keyL[QUEUE_READ(queueM)]);
        outAt+=keyL[QUEUE_READ(queueM)];
       
        --queueM;
    }
   
    //successful end
    out[outAt++]='\n';
    return 1;
}

// ---------------

static inline void parse_line(const char *in,const long l)
{
    switch(in[0])
    {
        case 'p':
       
            if( ITE_CMP("ublic",5) || ITE_CMP("rivate",6) )
            {
                unsigned i=7;
                while(in[i]<=32&&i!=l)++i;//skip whitespace
                if(i!=l) parse_line(&in[i],l-i);
            }
            break;
           
        case 'c':
       
            if(    ITE_CMP("onstant",7) )
            {
                unsigned i=9;
                while(in[i]<=32&&i!=l)++i;//skip whitespace
                if(i!=l) parse_line(&in[i],l-i);
            }
            break;
           
        case 's':
           
            if (ITE_CMP("tatic",5))
            {
                unsigned i=7;
                while(in[i]<=32&&i!=l)++i;//skip whitespace
                if(i!=l) parse_line(&in[i],l-i);
            }
            else ITE_BLOCK("cope",4,2)
            else ITE_BLOCK("truct",5,0)
            break;
           
        case 'i':
           
            ITE_BLOCK("f",1,7)
            break;
           
        case 'm':
           
            ITE_BLOCK("ethod",5,5)
            else ITE_BLOCK("odule",5,8)
            break;
           
        case 'g':
       
            ITE_BLOCK("lobals",6,4)
            break;
       
        case 'l':
           
            ITE_BLOCK("oop",3,6)
            ITE_BLOCK("ibrary",6,1)
            break;
           
        case 'f':
           
            ITE_BLOCK("unction",7,3)
            break;
    }
}

// ---------------

static inline void clear(char **in,unsigned long *l)    // removes delimited comments
{
    long length=0;    //new l
    char *out;        //output
   
    char *start,*end=*in;
   
    if((start=strstr(end,"/*"))==NULL) return;
    badlines=1;
   
    out=malloc(sizeof(char)*(*l));//we already know that it has "/*" to cut out, so no need for l+1
    memcpy(out,end,start-end);
    length+=start-end;
   
    while((end=strstr(start,"*/"))!=NULL)
    {
        if((start=strstr(end,"/*"))==NULL)
        {
            memcpy(&out[length],&end[2],&(*in)[*l]-end-2);
            length+=&(*in)[*l]-end-2;
            break;
        }
        else
        {
            memcpy(&out[length],&end[2],start-end-2);
            length+=start-end-2;
        }
    }
   
    free(*in);
    *l=length;
    *in=out;
    out[length]='\0';
}

// ---------------

static inline int line_type(const char *in,const long l)
{
    for(int i=0;i<l;++i)
    {
        if(in[i]>32) return !!memcmp(&in[i],"//",2);
    }
    return 2;
}
I use GCC to compile it, and I've tested on w7 x64. Please tell your OS if you get it to work, thanks!
 

Attachments

  • vjassindent.rar
    15.8 KB · Views: 38
Level 13
Joined
Nov 7, 2014
Messages
570
I made this little tool that converts intendation based vJASS into normal vJASS, without compromising readability.

I don't see the point of making vJass (a "block" based language) into a "off-side rule" language.

JASS:
library ayy requires optional SpellEvent

    //this is globals block
    globals
        private constant integer spellId='A000'

    private struct Ayy extends array

        static method onCast takes nothing returns nothing
            //its the spell!

        static if(LIBRARY_SpellEvent)

            //init with spellevent
            static method onInit takes nothing returns nothing
                call SpellEvent.create(spellId,function thistype.onCast)

        else

            static method onAnyCast takes nothing returns boolean
                if(GetSpellAbilityId()==spellId)then
                    call onCast()
                return false

            //init without spellevent
            static method onInit takes nothing returns nothing
                local trigger t=CreateTrigger()
                call TriggerRegisterAnyUnitEventBJ(t,EVENT_PLAYER_UNIT_SPELL_EFFECT)
                call TriggerAddCondition(t,Condition(function thistype.onAnyCast))
                set t=null

Even in short examples it's hard (for me at least), to tell where one block starts and where it ends...
In "block" based and curly based languages you have a token to "guide" your eyes (and some editors can jump back and forth between the "opening" block token and the "end" block token),
but in the "off-side" rule languages there's nothing... You have to "imagine it" or something... I don't know...

Also if here's people who know C, I'd appreciate to go over the script of the tool (in my most recent post on this thread) and give me feedback on how it looks.

I don't really know C, but I know that it has enums.

Code:
switch(fix)
{
    case 1://floor
        dNow=dNow/TAB_SIZE*TAB_SIZE;
        break;
    case 2://ceil
        dNow=dNow/TAB_SIZE*TAB_SIZE+TAB_SIZE;
        break;
    default:
        memcpy(err,&in[index+whiteL],lineL);
        printf("vjassindent: unable to parse %s, bad indentation:\n line %s%u: %s\n",fname,(badlines)?"~":"",curL,err);
        return 0;
}

I.e instead of using magical numbers (1 = floor, 2 = ceil):
Code:
typedef enum {
    Fix_Type_FLOOR,
    Fix_Type_CEIL,
} Fix_Type;

...
Fix_Type fix_type = ...;

switch(fix_type)
{
    case Fix_Type_FLOOR:
        ...
        break;
    case Fix_Type_CEIL
        ...
        break;
    default:
    ...
}
...
You can use an enum.

You can also get rid of the #defines that are integer constants and use an enum for those as well, because I don't think #defines show in the debugger (maybe they do, like I said, I don't really know C).

Also, it looks bad (in my opinion) when you omit the whitespace between operators and the commas:
Code:
memcpy(err,&in[index+whiteL],lineL);

// vs

memcpy(err, &in[index + whiteL], lineL);

I did try numerous languages occasionally, including big ones Java and C++.
...
I just came across C couple weeks ago and I love its simplicity.
Where/How did you learn about C++ without C being mentioned at all?

And about it's simplicity... is it really simple? In your program you do a lot of string manipulation and C is notoriously bad in this regard because it's arrays (strings being arrays of bytes) don't know their length and they don't have bounds checking, even in debug build... and those 2 things make C a very difficult language in my opinion.

Also, it's statically typed, yet type unsafe (even Python is probably more type safe, and it's not a statically typed language, although it's an "off-side rule" one),
for example the Fix_Type enum above, well you can assign plain integers to a variable of that type:

Code:
Fix_Type ft = 42; // not an error

There's probably a way to tell the compiler to warn you about it, but I don't know it and it's not the default.
Also in a switch statement if we omitted the Fix_Type_CEIL case we won't get a warning (I think), maybe there's a flag for that as well...

I'm way more ambitious than this (you have to start small :) ). I seek to create an entire vJass enhancement suite, including some new additions like generics, something like:
JASS:
struct LinkedList<Type> extends array
    readonly <Type> data
    readonly integer n
    readonly integer p
    //...
endstruct

There are at least two other projects here on hive that I think have something like this: WurstScript (heh, another "off-side" rule one...) and vrJass.
 

Deleted member 219079

D

Deleted member 219079

@Aniki Thanks for the review!

The subject of the tool is more or less secondary matter :p the objective with this vjassindent.exe is to get me started on text parsers and improve my C skills. But purpose-wise with vjassindent my goal is to make it able to convert text so well that you can't tell if it's written in vanilla vJASS or in indentation-based syntax.

WurstScript and vrJass I believe have different goals. They seek to replace vJASS. I want to enhance vJASS and maintain 100% compatibility (which already is not true because of delimited comments, but we'll see in the future version...). Also I want the users themselves to decide what enhancements they use. For example if I have the generic-addition someday, you don't have to use indentation-syntax and vice versa.

I forgot about enums lol. I have one in there for the starters.

Where/How did you learn about C++ without C being mentioned at all?
They did mention C. But I went through C++ tutorials with mindset of "ok, this is my future I get good job if I learn this well". Basically from the get-go I thought of C++ as my go-to language. Also some of the tutorials mentioned C as the predecessor of C++ (which is not true!!) so that repulsed me from C as well.

About arrays: Yeah I wonder why C doesn't know the array size? Since with free(myPointer) you don't define the amount of bytes to free. So there must be data for the malloc() sizes.

Also, it looks bad (in my opinion) when you omit the whitespace between operators and the commas:
Haha it's a habit of mine which carries from my vJASS scripting. In any group projects I might attend to in future I might have to re-evaluate some of my writing conventions. For now it's more natural way for me.

---------------


-Empty lines consequent to a comment no longer count towards appended empty lines
-Parser no longer stops at erratic indentation of a non-code line
-Replace feature has been renovated
-Code is now in two files to ease maintaining
1.0.1b: Fixed a bug where non-code lines would change order
Code:
//    vjassindent v1.0.1
//    by jondrean

// date: 20.06.16
// updated: 24.06.16

// ---------------

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

// ---------------

#define TAB_SIZE                 4        //this many spaces is tab
#define MAX_INDENT                 10     //this many tabs is max
#define BUFF_START                4096    //how many bytes prepared for file reading?

static const char* HELP_STR     =
    "Usage: vjassindent file1.j ... -o output.j\n"
    "\t--fix-by-floor,\n"
    "\t--fix-by-ceil   \teliminate bad indentation\n"
    "\t--help          \tshow this text\n"
    "\t--omit-comments \tdon't output comments\n"
    "\t-replace .ext   \toutput will be fname.ext\n"
    ;

// ---------------

static inline void clear(char **in,unsigned long *l);

// ---------------

int badlines=0;    //=has delimited comments
int nocomments=0;
enum {FIX_NONE,FIX_FLOOR,FIX_CEIL} fix=FIX_NONE;
char *out;
unsigned long outL;
unsigned long outAt;
char *tabs[MAX_INDENT];

// ---------------

#include "parser.h"

// ---------------

int main(int argc,char **argv)
{
    int replace = 0;
    FILE *tmp     = NULL;
    long buffL;
    char *buff;
    char *ext;
    size_t extL;

    //give help
    if(argc==1)
    {
        printf(HELP_STR);
        system("pause");    //might be opened from explorer
        return 0;
    }
    else if(argc==2&&!strcmp(argv[1],"--help"))
    {
        printf(HELP_STR);
        return 0;
    }

    //read arguments
    for(int i=1;i<argc;++i)
    {
        int a=0;
        if((a=!strcmp(argv[i],"-o"))||!strcmp(argv[i],"-replace"))
        {
            ++i;
            if(i==argc)
            {
                printf("vjassindent: expected specification after \"%s\"",argv[i-1]);
                return 1;
            }
        
            if(a)
            {
                tmp = fopen(argv[i],"w");
                if(tmp==NULL)
                {
                    printf("vjassindent: unable to create output file\n");
                    return 1;
                }
            }
            else
            {
                replace     = 1;
                ext         = argv[i];
                extL         = strlen(ext);
            }
    
            argc-=2;
            for(int j=i-1;j<argc;++j) argv[j]=argv[j+2];
            i-=2;
        }
        else if(!strcmp(argv[i],"--omit-comments")) nocomments=1;
        else if(strlen(argv[i])>9&&!memcmp(argv[i],"--fix-by-",9))
        {
            if(!strcmp(&(argv[i])[9],"ceil"))         fix = FIX_CEIL;
            else if(!strcmp(&(argv[i])[9],"floor")) fix = FIX_FLOOR;
        }
        else if(!strcmp(argv[i],"--replace")) replace=1;
        else continue;

    }

    //give help (didn't specify output file's path)
    if((replace)?tmp!=NULL:tmp==NULL)
    {
        printf("vjassindent: %s output\n",(replace)?"doubly declared":"no");
        return 1;
    }

    //get buffer
    outL     = BUFF_START;
    out     = malloc(sizeof(char)*(outL+1));
    buffL     = BUFF_START;
    buff     = malloc(sizeof(char)*(buffL+1));
    if(out==NULL)
    {
        printf("vjassindent: unable to allocate buffer\n");
        return 1;
    }

    //form tabs
    tabs[MAX_INDENT-1]=malloc(sizeof(char)*MAX_INDENT);
    for(int i=MAX_INDENT;--i;)
    {
        tabs[MAX_INDENT-1][i]='\t';
        tabs[MAX_INDENT-i]=&(tabs[MAX_INDENT-1])[i];
    }
    tabs[0]=&tabs[MAX_INDENT-1][0];

    //go through given files
    for(int i=1;i<argc;++i)
    {
        //skip flags
        if(!memcmp(argv[i],"--",2)) continue;
    
        FILE *f=fopen(argv[i],"r");
        if(f==NULL)
        {
            printf("vjassindent: unable to open %s\n",argv[i]);
            return 1;
        }

        //get file length
        fseek(f,0,SEEK_END);
        unsigned long l=ftell(f);
        fseek(f,0,SEEK_SET);

        //make sure output buffer can take it
        while(l*3/2>outL-outAt)
        {
            if((out=realloc(out,sizeof(char)*(outL*=2+1)))==NULL)
            {
                printf("vjassindent: unable to reallocate write buffer");
                return 1;
            }
        }

        //make sure read buffer can take it
        if(buffL<l)
        {
            buffL=l;
            buff=realloc(buff,sizeof(char)*buffL);
            if(buff==NULL)
            {
                printf("vjassindent: unable to reallocate read buffer\n");
                return 1;
            }
        }

        //read file and clear
        l=fread(buff,sizeof(char),l,f);
        buff[l]='\0';
        clear(&buff,&l);

        //do parse
        if(!parse(buff,l,argv[i])) return 1;

        //write to file (if -replace .ext)
        if(replace)
        {
            //get length of name base
            char *extpos     = strstr(argv[i],".");
            long baseNameL     = (extpos==NULL)?strlen(argv[i]):extpos-argv[i];
    
            //name with .ext
            char *newname     = malloc(sizeof(char)*(baseNameL+extL+1));
            memcpy(newname,argv[i],baseNameL);
            memcpy(&newname[baseNameL],ext,extL);
    
            //open the new file
            f=freopen(newname,"w",f);
            if(!fwrite(out,outAt-2,sizeof(char),f))
            {
                char err[1000];
                sprintf(err,"vjassindent: failed to write to output file \"%s\"\n",newname);
                printf(err);
                return 1;
            }
            outAt=0;
        }
        fclose(f);
    }
    if(!replace)
    {
        if(!fwrite(out,outAt-2,sizeof(char),tmp))
        {
            printf("vjassindent: failed to write to output file\n");
            return 1;
        }
        fclose(tmp);
    }

    //return success
    free(buff);
    free(out);
    return 0;
}

// ---------------

static inline void clear(char **in,unsigned long *l)    // removes delimited comments
{
    long length=0;    //new l
    char *out;        //output

    char *start,*end=*in;

    if((start=strstr(end,"/*"))==NULL) return;
    badlines=1;

    out=malloc(sizeof(char)*(*l));//we already know that it has "/*" to cut out, so no need for l+1
    memcpy(out,end,start-end);
    length+=start-end;

    while((end=strstr(start,"*/"))!=NULL)
    {
        if((start=strstr(end,"/*"))==NULL)
        {
            memcpy(&out[length],&end[2],&(*in)[*l]-end-2);
            length+=&(*in)[*l]-end-2;
            break;
        }
        else
        {
            memcpy(&out[length],&end[2],start-end-2);
            length+=start-end-2;
        }
    }

    free(*in);
    *l=length;
    *in=out;
    out[length]='\0';
}
Code:
#ifndef _PARSER_H_
#define _PARSER_H_

const char *KEYS[9]= 
{
    "endstruct\n",
    "endlibrary\n",
    "endscope\n",
    "endfunction\n",
    "endglobals\n",
    "endmethod\n",
    "endloop\n",
    "endif\n",
    "endmodule\n"
};
const unsigned KEY_LENGTH[9]    = {10,11,9,12,11,10,8,6,10};
const int IF_INDEX                 = 7; //recognize if to properly handle elses

int emptys[MAX_INDENT]        = {0};    //how many empty lines after end<ele>?
char emptysLock[MAX_INDENT]    = {1};    //no more counting empty lines?

int queueM=-1;
unsigned char queue[MAX_INDENT];    //what key each indentation wants?

// ---------------

static inline void parse_line(const char *in,const long l);    // returns the index it left at
static inline int line_type(const char *in,const long l);    // 1=real line; 0=has comments; 2=empty line

// ---------------

//this is used in "parse"
#define QUEUE_READ(i) queue[i]

//these are used in "parse_line"
#define QUEUE_PUSH(i) {queue[++queueM]=i;emptysLock[queueM]=0;}
#define ITE_CMP(to,len) (l>len && !memcmp(&in[1],to,len))
#define ITE_BLOCK(to,len,i) if(ITE_CMP(to,len)) QUEUE_PUSH(i)

// ---------------

//lines awaiting for normal line
unsigned    stackC = 0;
char         *stack[50];
int         stackL[50];

//stacking lines
#define PARSER_NONCODE_LINE() \
    do \
    { \
        if(!stackC&&dNow>queueM*TAB_SIZE) \
        { \
            memcpy(&out[outAt],&in[index],lineL+whiteL); \
            outAt         += lineL+whiteL; \
            out[outAt++] = '\n'; \
        } \
        else \
        { \
            stack[stackC]                = &in[index]; \
            stackL[stackC]                = whiteL+lineL+1; \
            stack[stackC][whiteL+lineL]    = '\n'; \
            ++stackC; \
            if(stackC==50) \
            { \
                printf("vjassindent: file %s exceeded pending line limit\n",fname); \
                return 1; \
            } \
        } \
    }while(0)

// ---------------

int dNow=0; //depth
static inline int parse(char *in,const long l,const char *fname)
{
    char *newlinepos;
    unsigned long index=0;    //reader pos
    unsigned lineL;
    unsigned curL=0;        //for error reporting
   
    int tempp=0;
   
       
    //go through given string "in"
    if(index!=l)
    {
        get_line:
        ++curL;
        newlinepos             = memchr(&in[index],'\n',l-index);
        lineL                 = (newlinepos==NULL) ? l-index : newlinepos-&in[index];
       
        //get whitespace, get indentation
        dNow=0;
        unsigned whiteL=0;    //depth, in bytes
        while(in[index+whiteL]<=32&&whiteL!=lineL)
        {
            if(in[index+whiteL]!='\t') ++dNow;
            else dNow+=TAB_SIZE;
            ++whiteL;
        }
        lineL-=whiteL;
       
        //what kind of line?
        switch(line_type(&in[index+whiteL],lineL))
        {
            case 2://empty line
                if(!nocomments&&queueM!=-1&&!emptysLock[queueM]) ++emptys[queueM];
                PARSER_NONCODE_LINE();
                break;
           
            case 0://has comments
                if(queueM!=-1)    emptysLock[queueM]=1;
                if(!nocomments)    PARSER_NONCODE_LINE();
                break;
               
            case 1://code line
       
                if(dNow%TAB_SIZE)
                {
                    char err[lineL];
                    switch(fix)
                    {
                        case FIX_FLOOR:
                            dNow=dNow/TAB_SIZE*TAB_SIZE;
                            break;
                        case FIX_CEIL:
                            dNow=dNow/TAB_SIZE*TAB_SIZE+TAB_SIZE;
                            break;
                        default:
                            memcpy(err,&in[index+whiteL],lineL);
                            printf("vjassindent: unable to parse %s, bad indentation:\n line %s%u: %s\n",fname,(badlines)?"~":"",curL,err);
                            return 0;
                    }
                }
                dNow/=TAB_SIZE;
               
                if(queueM!=-1)emptysLock[queueM]=1;
               
                //code lines need to have good indentation
                if(dNow>MAX_INDENT)
                {
                    char err[lineL];
                    memcpy(err,&in[index+whiteL],lineL);
                    printf("vjassindent: unable to parse %s, too big indentation:\n line %s%u: %s\n",fname,(badlines)?"~":"",curL,err);
                    return 0;
                }
               
                //this is where we append "end<ele>:s"
                while(queueM>=dNow)
                {
                    //postpone "endif"
                    if(        queueM==dNow
                        &&    QUEUE_READ(queueM)==IF_INDEX
                        &&    lineL>=4&&!memcmp(&in[index+whiteL],"else",4))
                        break;
                   
                    //append empty lines
                    while(emptys[queueM])
                    {
                        memcpy(&out[outAt],tabs[queueM+1],queueM+1);
                        outAt         += queueM+1;
                        out[outAt++] = '\n';
                        --emptys[queueM];
                    }
                   
                    //append indentation
                    memcpy(&out[outAt],tabs[queueM],queueM);
                    outAt         += queueM;
                   
                    //append "end<ele>"
                    memcpy(&out[outAt],KEYS[QUEUE_READ(queueM)],KEY_LENGTH[QUEUE_READ(queueM)]);
                    outAt+=KEY_LENGTH[QUEUE_READ(queueM)];
                   
                    --queueM;
                }
               
                //append any lines after end<ele>
                for(int i=0;i<stackC;++i)
                {
                    memcpy(&out[outAt],stack[i],stackL[i]);
                    outAt    += stackL[i];
                }
                stackC=0;
               
                //look for keywords
                parse_line(&in[index+whiteL],lineL);
               
                //append current line
                memcpy(&out[outAt],&in[index],lineL+whiteL);
                outAt         += lineL+whiteL;
                out[outAt++] = '\n';
        }
       
        index=newlinepos-in+1;
        if(newlinepos!=NULL)goto get_line;
        else goto final_append;
    }
   
    //append any remaining "end<ele>:s"
    final_append:
    while(queueM>=0)
    {
        //append empty lines
        while(emptys[queueM])
        {
            memcpy(&out[outAt],tabs[queueM+1],queueM+1);
            outAt         += queueM+1;
            out[outAt++] = '\n';
            --emptys[queueM];
        }
       
        //append indentation
        memcpy(&out[outAt],tabs[queueM],queueM);
        outAt         += queueM;
       
        //append "end<ele>"
        memcpy(&out[outAt],KEYS[QUEUE_READ(queueM)],KEY_LENGTH[QUEUE_READ(queueM)]);
        outAt+=KEY_LENGTH[QUEUE_READ(queueM)];
       
        --queueM;
    }
               
    //append any lines after end<ele>
    for(int i=0;i<stackC;++i)
    {
        memcpy(&out[outAt],stack[i],stackL[i]);
        outAt    += stackL[i];
    }
    stackC=0;
   
    //successful end
    out[outAt++]='\n';
    return 1;
}

// ---------------

static inline void parse_line(const char *in,const long l)
{
    switch(in[0])
    {
        case 'p':
       
            if( ITE_CMP("ublic",5) || ITE_CMP("rivate",6) )
            {
                unsigned i=7;
                while(in[i]<=32&&i!=l)++i;//skip whitespace
                if(i!=l) parse_line(&in[i],l-i);
            }
            break;
           
        case 'c':
       
            if(    ITE_CMP("onstant",7) )
            {
                unsigned i=9;
                while(in[i]<=32&&i!=l)++i;//skip whitespace
                if(i!=l) parse_line(&in[i],l-i);
            }
            break;
           
        case 's':
           
            if (ITE_CMP("tatic",5))
            {
                unsigned i=7;
                while(in[i]<=32&&i!=l)++i;//skip whitespace
                if(i!=l) parse_line(&in[i],l-i);
            }
            else ITE_BLOCK("cope",4,2)
            else ITE_BLOCK("truct",5,0)
            break;
           
        case 'i':
           
            ITE_BLOCK("f",1,7)
            break;
           
        case 'm':
           
            ITE_BLOCK("ethod",5,5)
            else ITE_BLOCK("odule",5,8)
            break;
           
        case 'g':
       
            ITE_BLOCK("lobals",6,4)
            break;
       
        case 'l':
           
            ITE_BLOCK("oop",3,6)
            ITE_BLOCK("ibrary",6,1)
            break;
           
        case 'f':
           
            ITE_BLOCK("unction",7,3)
            break;
    }
}

// ---------------

static inline int line_type(const char *in,const long l)
{
    for(int i=0;i<l;++i)
    {
        if(in[i]>32) return !!memcmp(&in[i],"//",2);    // >32 = past whitespace
    }
    return 2;    // 2=empty line
}

#endif

Again compiled as attachment below, which works with my w7 x64. Please tell your OS if you have it working, thanks!
 

Attachments

  • vjassindent-1.0.1b.rar
    16.3 KB · Views: 29
Last edited by a moderator:
Level 28
Joined
Jul 29, 2007
Messages
5,134
Sure, C is nice and simple, which can be charming at first, until you realize that this simplicity makes your code horrible with endless pointers, manual memory management, prefixes for everything since you have only PODs (aka struct), and a bad standard library.
To make matters worse, it is especially bad with anything related to string manipulations, where in the end you do most of the pointer management manually.

If you are looking at programming as your future, definitely use C++ (or you might look at less annoying languages, ranging from C#/Go!/etc. on the statically typed side, and JavaScript/Python/Ruby/etc. on the dynamically typed side), but only bother with the new versions (11+), and definitely don't learn from sites that think that C++ is C with classes, because the resulting code will be C with classes, which is the worst.
 

Deleted member 219079

D

Deleted member 219079

GhostWolf I will take a look at C++ again someday for sure. It's good for having good chances at getting a job like you said.

Almia I can't move the thread, you can redirect your concern to a mod :) (note that this is WIP as long as I don't feel like it's ready, it's ready after I have delimited comment support)

Ruke, the point of this project is for me to learn C. I can use a library, or I can learn how to do it myself. But ok now that that's done, let's say version 2.0.0 will use libraries instead to reduce code, now can you suggest me what libraries to use?
edit: By the way, the tool works well with your vrJass as well haha

---------------


-Non-code lines are positioned more sensibly
Code:
//    vjassindent v1.0.1c
//    by jondrean

// date: 20.06.16
// updated: 24.06.16

// ---------------

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

// ---------------

#define TAB_SIZE                 4        //this many spaces is tab
#define MAX_INDENT                 10     //this many tabs is max
#define BUFF_START                4096    //how many bytes prepared for file reading?

static const char* HELP_STR     =
    "Usage: vjassindent file1.j ... -o output.j\n"
    "\t--fix-by-floor,\n"
    "\t--fix-by-ceil   \teliminate bad indentation\n"
    "\t--help          \tshow this text\n"
    "\t--omit-comments \tdon't output comments\n"
    "\t-replace .ext   \toutput will be fname.ext\n"
    ;

// ---------------

static inline void clear(char **in,unsigned long *l);

// ---------------

int badlines=0;    //=has delimited comments
int nocomments=0;
enum {FIX_NONE,FIX_FLOOR,FIX_CEIL} fix=FIX_NONE;
char *out;
unsigned long outL;
unsigned long outAt;
char *tabs[MAX_INDENT];

// ---------------

#include "parser.h"

// ---------------

int main(int argc,char **argv)
{
    int replace = 0;
    FILE *tmp     = NULL;
    long buffL;
    char *buff;
    char *ext;
    size_t extL;

    //give help
    if(argc==1)
    {
        printf(HELP_STR);
        system("pause");    //might be opened from explorer
        return 0;
    }
    else if(argc==2&&!strcmp(argv[1],"--help"))
    {
        printf(HELP_STR);
        return 0;
    }

    //read arguments
    for(int i=1;i<argc;++i)
    {
        int a=0;
        if((a=!strcmp(argv[i],"-o"))||!strcmp(argv[i],"-replace"))
        {
            ++i;
            if(i==argc)
            {
                printf("vjassindent: expected specification after \"%s\"",argv[i-1]);
                return 1;
            }
            
            if(a)
            {
                tmp = fopen(argv[i],"w");
                if(tmp==NULL)
                {
                    printf("vjassindent: unable to create output file\n");
                    return 1;
                }
            }
            else
            {
                replace     = 1;
                ext         = argv[i];
                extL         = strlen(ext);
            }
        
            argc-=2;
            for(int j=i-1;j<argc;++j) argv[j]=argv[j+2];
            i-=2;
        }
        else if(!strcmp(argv[i],"--omit-comments")) nocomments=1;
        else if(strlen(argv[i])>9&&!memcmp(argv[i],"--fix-by-",9))
        {
            if(!strcmp(&(argv[i])[9],"ceil"))         fix = FIX_CEIL;
            else if(!strcmp(&(argv[i])[9],"floor")) fix = FIX_FLOOR;
        }
        else if(!strcmp(argv[i],"--replace")) replace=1;
        else continue;
    
    }
    
    //give help (didn't specify output file's path)
    if((replace)?tmp!=NULL:tmp==NULL)
    {
        printf("vjassindent: %s output\n",(replace)?"doubly declared":"no");
        return 1;
    }

    //get buffer
    outL     = BUFF_START;
    out     = malloc(sizeof(char)*(outL+1));
    buffL     = BUFF_START;
    buff     = malloc(sizeof(char)*(buffL+1));
    if(out==NULL)
    {
        printf("vjassindent: unable to allocate buffer\n");
        return 1;
    }

    //form tabs
    tabs[MAX_INDENT-1]=malloc(sizeof(char)*MAX_INDENT);
    for(int i=MAX_INDENT;--i;)
    {
        tabs[MAX_INDENT-1][i]='\t';
        tabs[MAX_INDENT-i]=&(tabs[MAX_INDENT-1])[i];
    }
    tabs[0]=&tabs[MAX_INDENT-1][0];

    //go through given files
    for(int i=1;i<argc;++i)
    {
        //skip flags
        if(!memcmp(argv[i],"--",2)) continue;
        
        FILE *f=fopen(argv[i],"r");
        if(f==NULL)
        {
            printf("vjassindent: unable to open %s\n",argv[i]);
            return 1;
        }
    
        //get file length
        fseek(f,0,SEEK_END);
        unsigned long l=ftell(f);
        fseek(f,0,SEEK_SET);
    
        //make sure output buffer can take it
        while(l*3/2>outL-outAt)
        {
            if((out=realloc(out,sizeof(char)*(outL*=2+1)))==NULL)
            {
                printf("vjassindent: unable to reallocate write buffer");
                return 1;
            }
        }
    
        //make sure read buffer can take it
        if(buffL<l)
        {
            buffL=l;
            buff=realloc(buff,sizeof(char)*buffL);
            if(buff==NULL)
            {
                printf("vjassindent: unable to reallocate read buffer\n");
                return 1;
            }
        }
    
        //read file and clear
        l=fread(buff,sizeof(char),l,f);
        buff[l]='\0';
        clear(&buff,&l);
    
        //do parse
        if(!parse(buff,l,argv[i])) return 1;
    
        //write to file (if -replace .ext)
        if(replace)
        {
            //get length of name base
            char *extpos     = strstr(argv[i],".");
            long baseNameL     = (extpos==NULL)?strlen(argv[i]):extpos-argv[i];
        
            //name with .ext
            char *newname     = malloc(sizeof(char)*(baseNameL+extL+1));
            memcpy(newname,argv[i],baseNameL);
            memcpy(&newname[baseNameL],ext,extL);
        
            //open the new file
            f=freopen(newname,"w",f);
            if(!fwrite(out,outAt-2,sizeof(char),f))
            {
                char err[1000];
                sprintf(err,"vjassindent: failed to write to output file \"%s\"\n",newname);
                printf(err);
                return 1;
            }
            outAt=0;
        }
        fclose(f);
    }
    if(!replace)
    {
        if(!fwrite(out,outAt-2,sizeof(char),tmp))
        {
            printf("vjassindent: failed to write to output file\n");
            return 1;
        }
        fclose(tmp);
    }

    //return success
    free(buff);
    free(out);
    return 0;
}

// ---------------

static inline void clear(char **in,unsigned long *l)    // removes delimited comments
{
    long length=0;    //new l
    char *out;        //output

    char *start,*end=*in;

    if((start=strstr(end,"/*"))==NULL) return;
    badlines=1;

    out=malloc(sizeof(char)*(*l));//we already know that it has "/*" to cut out, so no need for l+1
    memcpy(out,end,start-end);
    length+=start-end;

    while((end=strstr(start,"*/"))!=NULL)
    {
        if((start=strstr(end,"/*"))==NULL)
        {
            memcpy(&out[length],&end[2],&(*in)[*l]-end-2);
            length+=&(*in)[*l]-end-2;
            break;
        }
        else
        {
            memcpy(&out[length],&end[2],start-end-2);
            length+=start-end-2;
        }
    }

    free(*in);
    *l=length;
    *in=out;
    out[length]='\0';
}
Code:
#ifndef _PARSER_H_
#define _PARSER_H_

const char *KEYS[9]=
{
    "endstruct\n",
    "endlibrary\n",
    "endscope\n",
    "endfunction\n",
    "endglobals\n",
    "endmethod\n",
    "endloop\n",
    "endif\n",
    "endmodule\n"
};
const unsigned KEY_LENGTH[9]    = {10,11,9,12,11,10,8,6,10};
const int IF_INDEX                 = 7; //recognize if to properly handle elses

int emptys[MAX_INDENT]        = {0};    //how many empty lines after end<ele>?
char emptysLock[MAX_INDENT]    = {1};    //no more counting empty lines?

int queueM=-1;
unsigned char queue[MAX_INDENT];    //what key each indentation wants?

// ---------------

static inline void parse_line(const char *in,const long l);    // returns the index it left at
static inline int line_type(const char *in,const long l);    // 1=real line; 0=has comments; 2=empty line

// ---------------

//this is used in "parse"
#define QUEUE_READ(i) queue[i]

//these are used in "parse_line"
#define QUEUE_PUSH(i) {queue[++queueM]=i;emptysLock[queueM]=0;}
#define ITE_CMP(to,len) (l>len && !memcmp(&in[1],to,len))
#define ITE_BLOCK(to,len,i) if(ITE_CMP(to,len)) QUEUE_PUSH(i)

// ---------------

//lines awaiting for normal line
unsigned    stackC = 0;
char         *stack[50];
int         stackL[50];

//append right away / stack line
#define PARSER_NONCODE_LINE() \
    do \
    { \
        if(!stackC&&dNow>=(queueM+1)*TAB_SIZE) \
        { \
            memcpy(&out[outAt],&in[index],lineL+whiteL); \
            outAt         += lineL+whiteL; \
            out[outAt++] = '\n'; \
        } \
        else \
        { \
            stack[stackC]                = &in[index]; \
            stackL[stackC]                = whiteL+lineL+1; \
            stack[stackC][whiteL+lineL]    = '\n'; \
            ++stackC; \
            if(stackC==50) \
            { \
                printf("vjassindent: file %s exceeded pending line limit\n",fname); \
                return 1; \
            } \
        } \
    }while(0)

// ---------------

int dNow=0; //depth
static inline int parse(char *in,const long l,const char *fname)
{
    char *newlinepos;
    unsigned long index=0;    //reader pos
    unsigned lineL;
    unsigned curL=0;        //for error reporting

    int tempp=0;

    
    //go through given string "in"
    if(index!=l)
    {
        get_line:
        ++curL;
        newlinepos             = memchr(&in[index],'\n',l-index);
        lineL                 = (newlinepos==NULL) ? l-index : newlinepos-&in[index];
    
        //get whitespace, get indentation
        dNow=0;
        unsigned whiteL=0;    //depth, in bytes
        while(in[index+whiteL]<=32&&whiteL!=lineL)
        {
            if(in[index+whiteL]!='\t') ++dNow;
            else dNow+=TAB_SIZE;
            ++whiteL;
        }
        lineL-=whiteL;
    
        //what kind of line?
        switch(line_type(&in[index+whiteL],lineL))
        {
            case 2://empty line
                if(!nocomments&&queueM!=-1&&!emptysLock[queueM]) ++emptys[queueM];
                PARSER_NONCODE_LINE();
                break;
        
            case 0://has comments
                if(queueM!=-1)    emptysLock[queueM]=1;
                if(!nocomments)    PARSER_NONCODE_LINE();
                break;
            
            case 1://code line
    
                if(dNow%TAB_SIZE)
                {
                    char err[lineL];
                    switch(fix)
                    {
                        case FIX_FLOOR:
                            dNow=dNow/TAB_SIZE*TAB_SIZE;
                            break;
                        case FIX_CEIL:
                            dNow=dNow/TAB_SIZE*TAB_SIZE+TAB_SIZE;
                            break;
                        default:
                            memcpy(err,&in[index+whiteL],lineL);
                            printf("vjassindent: unable to parse %s, bad indentation:\n line %s%u: %s\n",fname,(badlines)?"~":"",curL,err);
                            return 0;
                    }
                }
                dNow/=TAB_SIZE;
            
                if(queueM!=-1)emptysLock[queueM]=1;
            
                //code lines need to have good indentation
                if(dNow>MAX_INDENT)
                {
                    char err[lineL];
                    memcpy(err,&in[index+whiteL],lineL);
                    printf("vjassindent: unable to parse %s, too big indentation:\n line %s%u: %s\n",fname,(badlines)?"~":"",curL,err);
                    return 0;
                }
            
                //this is where we append "end<ele>:s"
                while(queueM>=dNow)
                {
                    //postpone "endif"
                    if(        queueM==dNow
                        &&    QUEUE_READ(queueM)==IF_INDEX
                        &&    lineL>=4&&!memcmp(&in[index+whiteL],"else",4))
                        break;
                
                    //append empty lines
                    while(emptys[queueM])
                    {
                        memcpy(&out[outAt],tabs[queueM+1],queueM+1);
                        outAt         += queueM+1;
                        out[outAt++] = '\n';
                        --emptys[queueM];
                    }
                
                    //append indentation
                    memcpy(&out[outAt],tabs[queueM],queueM);
                    outAt         += queueM;
                
                    //append "end<ele>"
                    memcpy(&out[outAt],KEYS[QUEUE_READ(queueM)],KEY_LENGTH[QUEUE_READ(queueM)]);
                    outAt+=KEY_LENGTH[QUEUE_READ(queueM)];
                
                    --queueM;
                }
            
                //append any lines after end<ele>
                for(int i=0;i<stackC;++i)
                {
                    memcpy(&out[outAt],stack[i],stackL[i]);
                    outAt    += stackL[i];
                }
                stackC=0;
            
                //look for keywords
                parse_line(&in[index+whiteL],lineL);
            
                //append current line
                memcpy(&out[outAt],&in[index],lineL+whiteL);
                outAt         += lineL+whiteL;
                out[outAt++] = '\n';
        }
    
        index=newlinepos-in+1;
        if(newlinepos!=NULL)goto get_line;
        else goto final_append;
    }

    //append any remaining "end<ele>:s"
    final_append:
    while(queueM>=0)
    {
        //append empty lines
        while(emptys[queueM])
        {
            memcpy(&out[outAt],tabs[queueM+1],queueM+1);
            outAt         += queueM+1;
            out[outAt++] = '\n';
            --emptys[queueM];
        }
    
        //append indentation
        memcpy(&out[outAt],tabs[queueM],queueM);
        outAt         += queueM;
    
        //append "end<ele>"
        memcpy(&out[outAt],KEYS[QUEUE_READ(queueM)],KEY_LENGTH[QUEUE_READ(queueM)]);
        outAt+=KEY_LENGTH[QUEUE_READ(queueM)];
    
        --queueM;
    }
            
    //append any lines after end<ele>
    for(int i=0;i<stackC;++i)
    {
        memcpy(&out[outAt],stack[i],stackL[i]);
        outAt    += stackL[i];
    }
    stackC=0;

    //successful end
    out[outAt++]='\n';
    return 1;
}

// ---------------

static inline void parse_line(const char *in,const long l)
{
    switch(in[0])
    {
        case 'p':
    
            if( ITE_CMP("ublic",5) || ITE_CMP("rivate",6) )
            {
                unsigned i=7;
                while(in[i]<=32&&i!=l)++i;//skip whitespace
                if(i!=l) parse_line(&in[i],l-i);
            }
            break;
        
        case 'c':
    
            if(    ITE_CMP("onstant",7) )
            {
                unsigned i=9;
                while(in[i]<=32&&i!=l)++i;//skip whitespace
                if(i!=l) parse_line(&in[i],l-i);
            }
            break;
        
        case 's':
        
            if (ITE_CMP("tatic",5))
            {
                unsigned i=7;
                while(in[i]<=32&&i!=l)++i;//skip whitespace
                if(i!=l) parse_line(&in[i],l-i);
            }
            else ITE_BLOCK("cope",4,2)
            else ITE_BLOCK("truct",5,0)
            break;
        
        case 'i':
        
            ITE_BLOCK("f",1,7)
            break;
        
        case 'm':
        
            ITE_BLOCK("ethod",5,5)
            else ITE_BLOCK("odule",5,8)
            break;
        
        case 'g':
    
            ITE_BLOCK("lobals",6,4)
            break;
    
        case 'l':
        
            ITE_BLOCK("oop",3,6)
            ITE_BLOCK("ibrary",6,1)
            break;
        
        case 'f':
        
            ITE_BLOCK("unction",7,3)
            break;
    }
}

// ---------------

static inline int line_type(const char *in,const long l)
{
    for(int i=0;i<l;++i)
    {
        if(in[i]>32) return !!memcmp(&in[i],"//",2);    // >32 = past whitespace
    }
    return 2;    // 2=empty line
}

#endif
 

Attachments

  • vjassindent-1.0.1c.rar
    16.3 KB · Views: 25
Last edited by a moderator:
Level 6
Joined
Jul 30, 2013
Messages
282
suggestion:
if you can also accept vanilla jass syntax in addition to the indent based one..
could you make a tool that could be used to auto-indent vanilla jass code?

this would be especially useful when converting GUI triggers to external script files because the defautl output has god-awful formatting and manually fixing it all can be tedious.

(some members of my team are not so good with scripting so they prefer the GUI editor but for version control (git) it is best to have external files, also can fix silly perf issues there better)

extra credits if it can handle vjass as well tho that is being greedy i guess :p
 

Deleted member 219079

D

Deleted member 219079

Ok, I made it so that upon coming across end<ele>, it pops top-most automated end<ele> from queue.

Too lazy to post code here (currently working on another project), you can see it from the .rar though.

You only need the .exe, as before.
 

Attachments

  • vjassindent-1.0.1d.rar
    17.5 KB · Views: 34

Deleted member 219079

D

Deleted member 219079

Hey guys, very good news! I think this will very soon be at a state I'm happy with. I've been learning C++ so I rewrote the tool in that. You can see the source.rar if you want to give me any advice.

Idk what the requirements for running C++ programs are, so tell me if it wants you to have a runtime or something like that.

Expect bugs; this is scratch-made

Edit: To integrate with JNGP, place the .exe to your jassnewgenpack - folder and make this change to your wehack.lua:
Code:
-- this will go in the place of your old compilemap_path in wehack.lua
function compilemap_path(mappath)
    if mappath == "" then
        showfirstsavewarning()
        return
    end
    map = wehack.openarchive(mappath,15)
    wehack.extractfile(jh_path.."jasshelper\\common.j","scripts\\common.j")
    wehack.extractfile(jh_path.."jasshelper\\Blizzard.j","scripts\\Blizzard.j")
    wehack.extractfile("war3map.j","war3map.j")
    wehack.closearchive(map)
    if cmdargs ~= "" then
        local cmdtable = argsplit(cmdargs)
grim.log("running tool on save: "..cmdargs)
        wehack.runprocess(cmdargs)
        cmdargs = ""
    end
    -- Here I'll add a new configuration for jasshelper. moyack
    if havejh and jh_enable.checked then
        toolresult = 0
        toolresult = wehack.runprocess2("vjassindent.exe war3map.j war3map.j")
        if toolresult==0 then
            cmdline = jh_path .. "jasshelper\\jasshelper.exe"
            if jh_debug.checked then
                cmdline = cmdline .. " --debug"
            end
            if jh_disable.checked then
                cmdline = cmdline .. " --nopreprocessor"
            end
            if jh_disableopt.checked then
                cmdline = cmdline .. " --nooptimize"
            end
               cmdline = cmdline .. " "..jh_path.."jasshelper\\common.j "..jh_path.."jasshelper\\blizzard.j ".."war3map.j \"".. mappath .."\""
            toolresult = 0
            toolresult = wehack.runprocess2(cmdline)
            if toolresult == 0 then
                mapvalid = true
            else
                mapvalid = false
            end
        else
           mapvalid = false
        end
    end
end

Edit2: The tool considers //! import :s now; if you want to use them, make "vjidirs.txt" (without quotes) to your jassnewgenpack - folder, here's example contents:
Code:
C:\path\to\jass\
D:\another\path\
 

Attachments

  • vjassindent-1.1.0.rar
    8.7 KB · Views: 24
  • source.rar
    2.9 KB · Views: 37
Last edited by a moderator:
Top