Jzip - GNU JavaScript Minifier

Status
Niet open voor verdere reacties.
Als er nog meer op- aan merkingen zijn hoor ik het graag.

De globale variabelen die je nu hebt zijn niet nodig, want ze worden slechts binnen ��n functie gebruikt. Verder zou ik de waardes waartegen je vergelijkt constant maken, zodat de code iets makkelijker leesbaar is.

[cpp]
const int FILE_ERROR = -1;
const int OPEN_P = '(';
const int EQUALS = '=';
const int COLON = ':';
const int ASTERISK = '*';
const int SLASH = '/';
const int BACKSLASH = '\\';
const int UNDERSCORE = '_';
const int DOLLARSIGN = '$';
const int NEW_LINE = '\n';
[/cpp]

Voor het opvragen van de grootte volstaat het om de bestandsnaam op te geven:
[cpp]
int get_file_size(const char *file)
{
FILE *fp = fopen(file, "r");
if (fp == NULL) return FILE_ERROR;
/* ... */
}
[/cpp]

Gebruik maken van de standaard-functies van de taal kan het ook leesbaarder maken:
[cpp]
int isalph(const int c)
{
return (isalnum(c) || c == UNDERSCORE || c == DOLLARSIGN | c == BACKSLASH || c > 126);
}
[/cpp]

[cpp]
void minjs(const char *input, const char *output)
{
/* ... */
int qwoted = 0, qwoted_type = 0, regexp = 0; // deze variabelen worden alleen binnen de minjs functie gebruikt -> een globale scope is dus overbodig
}
[/cpp]

Verder kan de code ook compacter worden geschreven als je goed kijkt naar wat je hebt en waar je naartoe wilt. Ik ga niet de hele code herschrijven, maar ik geef je wel een idee van wat ik bedoel:

[cpp]
/*
/* RegExp? */
if (c == '/')
{
/* ... */
}
/* Single or multi line comment? */

if (c == '/' && qwoted == 0 && regexp == 0)
[/cpp]
-> beide hebben als voorwaarde c == SLASH
[cpp]
if (c == SLASH)
{
if (regexp && (prevchar != BACKSLASH || prevchar == preprechar))
{
regexp = 0;
}
if (qwoted == 0 && (prevchar == OPEN_P || prevchar == EQUALS || prevchar == COLON))
{
regexp = 1;
}
if (regexp == 0 && qwoted == 0)
{
/* ... */
}
}
[/cpp]

[cpp]
/* Quoted ? */
if (c == '\'' && qwoted_type != 0 && prevchar != '\\' && regexp == 0 || c == '\'' && qwoted_type != 0 && preprechar != 0 && prevchar == '\\' && preprechar == '\\' && regexp == 0 || c == '"' && qwoted_type != 1 && prevchar != '\\' && regexp == 0 || c == '"' && qwoted_type != 1 && preprechar != 0 && prevchar == '\\' && preprechar == '\\' && regexp == 0)
{
if (qwoted)
{
qwoted = 0;
}
else
{
/* Set quote type */
if (c == '"')
{
qwoted_type = 0;
}
else if (c == '\'')
{
qwoted_type = 1;
}

qwoted = 1;
}
}
[/cpp]

c == '\'' && qwoted_type != 0 && prevchar != '\\' && regexp == 0 ||
c == '\'' && qwoted_type != 0 && preprechar != 0 && prevchar == '\\' && preprechar == '\\' && regexp == 0 ||
c == '"' && qwoted_type != 1 && prevchar != '\\' && regexp == 0 ||
c == '"' && qwoted_type != 1 && preprechar != 0 && prevchar == '\\' && preprechar == '\\' && regexp == 0

Je kunt de code in drie stukken opdelen: regexp == 0 en (c == SINGLE_QUOTE && qwoted_type != 0 || c == DOUBLE_QUOTE && qwoted_type != 1) en (prevchar != BACKSLASH || prevchar == preprechar). Deze kun je samenvoegen tot:

[cpp]
if (regexp == 0 &&
(c == SINGLE_QUOTE && qwoted_type != 0 || c == DOUBLE_QUOTE && qwoted_type != 1) &&
(prevchar != BACKSLASH || prevchar == preprechar))
{
if (qwoted == 0)
qwoted_type = (c == SINGLE_QUOTE ? 1 : 0);
qwoted ^= 1;
}
[/cpp]

Als er geen argument wordt opgegeven en je controleert dan 'argv[1] == NULL', dan spreek je een element aan dat niet binnen het bereik van de array ligt. argv[argc-1] is immers het laatste element binnen de array, welke bij 1 argument dus gelijk is aan argv[0]. Via argc is het mogelijk te zien hoeveel argumenten er meegegeven zijn en aan de hand daarvan actie te ondernemen:

[cpp]
int main(int argc, char **argv)
{
int warns = 0; // deze variabele wordt in je huidige code alleen binnen je main functie gebruikt -> een globale scope is dus overbodig
if (argc == 2)
{
// if (strncmp(argv[1], "help", 4) == 0) -> toon help
// if (get_file_size(argv[1]) != FILE_ERROR) -> het argument voor de invoer is gegeven, maar niet voor de uitvoer
}
else if (argc == 3)
{
// de argumenten voor de invoer/uitvoer zijn opgegeven en kunnen dus op de voorwaarden gecontroleerd worden
// bericht dat het proces wordt gestart (stel dat je beslist om er een Windows-applicatie van te maken, dan heb je niks aan de printf functie die je nu gebruikt binnen minjs)
minjs(argv[1], argv[2]);
// bericht dat het proces voltooid is
}
else
{
// toon bericht over het programma gebruikt moet worden (volstaat ook indien er teveel argumenten worden meegegeven)
}
return 0;
}
[/cpp]

edit: de code is niet getest en ik zag een fout zitten welke ik verbeterd heb.
 
Laatst bewerkt:
Bedankt voor de annalize :)


De globale variabelen die je nu hebt zijn niet nodig, want ze worden slechts binnen ��n functie gebruikt. Verder zou ik de waardes waartegen je vergelijkt constant maken, zodat de code iets makkelijker leesbaar is.

Helemaal mee eens.

Voor het opvragen van de grootte volstaat het om de bestandsnaam op te geven:
[cpp]
int get_file_size(const char *file)
{
FILE *fp = fopen(file, "r");
if (fp == NULL) return FILE_ERROR;
/* ... */
}
[/cpp]

Pointer naar file werkt toch ook?

Gebruik maken van de standaard-functies van de taal kan het ook leesbaarder maken:
[cpp]
int isalph(const int c)
{
return (isalnum(c) || c == UNDERSCORE || c == DOLLARSIGN | c == BACKSLASH || c > 126);
}
[/cpp]

Dat betekend wel dat je ctype.h moet includen en dat maakt je exe weer een stuk groter. Heeft het wel zin voor die paar letters code?

[cpp]
void minjs(const char *input, const char *output)
{
/* ... */
int qwoted = 0, qwoted_type = 0, regexp = 0; // deze variabelen worden alleen binnen de minjs functie gebruikt -> een globale scope is dus overbodig
}
[/cpp]


Verder kan de code ook compacter worden geschreven als je goed kijkt naar wat je hebt en waar je naartoe wilt. Ik ga niet de hele code herschrijven, maar ik geef je wel een idee van wat ik bedoel:

[cpp]
/*
/* RegExp? */
if (c == '/')
{
/* ... */
}
/* Single or multi line comment? */

if (c == '/' && qwoted == 0 && regexp == 0)
[/cpp]
-> beide hebben als voorwaarde c == SLASH

Weer mee eens, alleen het kost een wat tijd om daar even rustig naar te kijken. Zodra ik die heb zal ik het verbereren :)

[cpp]
if (c == SLASH)
{
if (regexp && (prevchar != BACKSLASH || prevchar == preprechar))
{
regexp = 0;
}
if (qwoted == 0 && (prevchar == OPEN_P || prevchar == EQUALS || prevchar == COLON))
{
regexp = 1;
}
if (regexp == 0 && qwoted == 0)
{
/* ... */
}
}
[/cpp]

[cpp]
/* Quoted ? */
if (c == '\'' && qwoted_type != 0 && prevchar != '\\' && regexp == 0 || c == '\'' && qwoted_type != 0 && preprechar != 0 && prevchar == '\\' && preprechar == '\\' && regexp == 0 || c == '"' && qwoted_type != 1 && prevchar != '\\' && regexp == 0 || c == '"' && qwoted_type != 1 && preprechar != 0 && prevchar == '\\' && preprechar == '\\' && regexp == 0)
{
if (qwoted)
{
qwoted = 0;
}
else
{
/* Set quote type */
if (c == '"')
{
qwoted_type = 0;
}
else if (c == '\'')
{
qwoted_type = 1;
}

qwoted = 1;
}
}
[/cpp]



Je kunt de code in drie stukken opdelen: regexp == 0 en (c == SINGLE_QUOTE && qwoted_type != 0 || c == DOUBLE_QUOTE && qwoted_type != 1) en (prevchar != BACKSLASH || prevchar == preprechar). Deze kun je samenvoegen tot:

[cpp]
if (regexp == 0 &&
(c == SINGLE_QUOTE && qwoted_type != 0 || c == DOUBLE_QUOTE && qwoted_type != 1) &&
(prevchar != BACKSLASH || prevchar == preprechar))
{
if (qwoted == 0)
qwoted_type = (c == SINGLE_QUOTE ? 1 : 0);
qwoted ^= 1;
}
[/cpp]

Als er geen argument wordt opgegeven en je controleert dan 'argv[1] == NULL', dan spreek je een element aan dat niet binnen het bereik van de array ligt. argv[argc-1] is immers het laatste element binnen de array, welke bij 1 argument dus gelijk is aan argv[0]. Via argc is het mogelijk te zien hoeveel argumenten er meegegeven zijn en aan de hand daarvan actie te ondernemen:

[cpp]
int main(int argc, char **argv)
{
int warns = 0; // deze variabele wordt in je huidige code alleen binnen je main functie gebruikt -> een globale scope is dus overbodig

if (argc == 2)
{
// if (strncmp(argv[1], "help", 4) == 0) -> toon help
// if (get_file_size(argv[1]) != FILE_ERROR) -> het argument voor de invoer is gegeven, maar niet voor de uitvoer
}
else if (argc == 3)
{
// de argumenten voor de invoer/uitvoer zijn opgegeven en kunnen dus op de voorwaarden gecontroleerd worden
// bericht dat het proces wordt gestart (stel dat je beslist om er een Windows-applicatie van te maken, dan heb je niks aan de printf functie die je nu gebruikt binnen minjs)
minjs(argv[1], argv[2]);
// bericht dat het proces voltooid is
}
else
{
// toon bericht over het programma gebruikt moet worden (volstaat ook indien er teveel argumenten worden meegegeven)
}
return 0;
}
[/cpp]

Misschien wat efficiënter, maar verder niet urgent.

Warn wordt trouwens wel in andere functies gebruikt. Dus tenzij je hem als extern wilt declareren in main, zou hij gewoon de globale scoop moeten behouden.

Jori.
 
Laatst bewerkt:
Ben a.t.m. bezig met versie 1.3.0.0. Als die af is zal ik hem plaatsen.
 
Versie 1.3.0.0 af. Hij is nu ietsjes sneller. 1.3.0.0 is ook geschikt gemaakt voor CSS, .css files.

Mocht iemand problemen hebben met het verkleinen van .css bestanden (die wel goed geprogrammeerd zijn!), graag even melden.


~ Jori.
 
Even een kleine opmerking: Ik weet niet zeker wat je programma doet, maar als ik het goed inschat verkort het in je javascript de variabelennamen etc., zodat een compact (zeg maar: onleesbaar/oninterpreteerbaar) script overblijft.

Als ik dat goed heb samengevat, dan loop je (bij gebruik) het risico dat webpagina's waar dergelijk gecomprimeerd javascript wordt gebruikt door (o.a.) AVG als 'gevaarlijk' worden beschouwd. Zie deze link, waar een bepaald Google javascript door AVG werd gedetecteerd als (mogelijk) gevaarlijk.

Tijs.
 
Dan zou zowat elke website gevaarlijk zijn. Heb je ooit iemand de jQuary library zien gebruiken zonder hem eerst te verkleinen? Bijna elke grote moderne website gebruikt jQuary.

Als virus scanners dat als een probleem zouden opvatten, zou er geen website meer over blijven.


~Jori.
 
Laatst bewerkt:
Ik meldde het alleen even, ter waarschuwing. :D
Misschien heeft Google een andere methode gebruikt om dat javascript zo compact/obscuur mogelijk te maken.

Het heeft niets met de werking van je programma te maken. Respect voor jou dat je een goed programma hebt gemaakt. :thumb:

Tijs.
 
Bedankt :)

Ik denk niet dat andere minifiers heel veel anders zijn, misschien meer opties maar meer ook niet.

Versie 1.3.0.0 zal uitgerust zijn met een variabelen verkleinen (bedankt voor het idee!).


~Jori.
 
In die discussiedraad die ik gelinkt hebt staat ook de link naar dat Google javascript:
https://ssl.google-analytics.com/ga.js
Als je die opslaat en dan bekijkt (heb ik natuurlijk ook gedaan), dan zul je zien dat het inderdaad behoorlijk 'onleesbaar' is (variabele-namen van maximaal 2 karakters etc.)

Dit dus voor de geïnteresseerden en misschien haalt Jorie13 er nog wat ideeën uit voor zijn eigen minifier.

Tijs.
 
Laatst bewerkt:
Heb alvast de volgende functie geschreven om variabelen te creëren aan de hand van een cijfer.

[CPP]/* Return a variable name in C style */
char* getvar(unsigned var_num)
{
/* Init with empty string */
char *var_name = "";

unsigned var_count = var_num;
int var_frst_char, var_sec_char;

/* Move to A */
var_count += 65;

/* One char */
if (var_num <= 52)
{
if (var_num == 26)
{
var_count = 95;
}
else if (var_num > 26)
{
var_count += 5;
}

/* Set char */
var_name[0] = (char) var_count;
}
/* Two chars */
else if (var_num > 52)
{
var_num += 10;
var_frst_char = var_num / 63;
var_sec_char = var_num % 63;

/* Init to 'A' */
var_count = 64 + var_frst_char;


if (var_frst_char == 26)
{
var_count = 95;
}
else if (var_frst_char > 26)
{
var_count += 5;
}

/* Set first char */
var_name[0] = (char) var_count;

/* Reset var_count */
var_count = 0;


var_count = 48 + var_sec_char;

if (var_sec_char > 9 && var_sec_char < 36)
{
var_count += 7;
}
else if (var_sec_char == 36)
{
var_count = 95;
}
else if (var_sec_char > 36)
{
var_count += 12;
}

/* Set second char */
var_name[1] = (char) var_count;
}

/* Return string name */
return var_name;
}[/CPP]


Waarbij geld:

[CPP]#define MAX_VARS 3391[/CPP]
 
Status
Niet open voor verdere reacties.
Terug
Bovenaan Onderaan