Created
May 9, 2018 14:33
-
-
Save IS4Code/da010608d20909d6eba2eeed9d27c3df to your computer and use it in GitHub Desktop.
JSON parser example using PawnPlus
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <a_samp> | |
#include <file> | |
#include <float> | |
#include <PawnPlus> | |
const json_ichar = -2; | |
const GlobalVariant:json_error_obj = GlobalVariant:-1; | |
static stock File:json_input; | |
static stock GlobalVariant:json_error(const msg[]) | |
{ | |
print(msg); | |
return json_error_obj; | |
} | |
static stock json_readchar() | |
{ | |
return fgetchar(json_input, 0); | |
} | |
static stock bool:json_isspace(c) | |
{ | |
switch(c) | |
{ | |
case json_ichar, ' ', '\t', '\v', '\n', '\r': | |
return true; | |
} | |
return false; | |
} | |
static stock json_readcharnospace() | |
{ | |
new c; | |
do{ | |
c = json_readchar(); | |
}while(json_isspace(c)); | |
return c; | |
} | |
static stock bool:json_testread(const str[], size=sizeof(str)) | |
{ | |
new len = size - 1; | |
for(new i = 0; i < len; i++) | |
{ | |
if(json_readchar() != str[i]) return false; | |
} | |
return true; | |
} | |
static stock GlobalVariant:json_readobj(&c) | |
{ | |
c = json_readcharnospace(); | |
switch(c) | |
{ | |
case '[': // list | |
{ | |
new List:list = list_new(); | |
for(;;) | |
{ | |
new GlobalVariant:obj = json_readobj(c); | |
if(_:obj == _:VAR_NULL && c == ']') | |
{ | |
break; | |
} | |
if(_:obj == _:json_error_obj) | |
{ | |
list_delete_deep(list); | |
return json_error_obj; | |
} | |
list_add_var(list, obj); | |
var_delete(obj); | |
if(json_isspace(c)) c = json_readcharnospace(); | |
if(c == ']') break; | |
if(c != ',') | |
{ | |
list_delete_deep(list); | |
return json_error("Invalid character found in a list."); | |
} | |
} | |
c = json_ichar; | |
return var_to_global(var_new(list)); | |
} | |
case '{': // map | |
{ | |
new Map:map = map_new(); | |
for(;;) | |
{ | |
new GlobalVariant:key = json_readobj(c); | |
if(_:key == _:VAR_NULL && c == '}') | |
{ | |
break; | |
} | |
if(_:key == _:json_error_obj) | |
{ | |
map_delete_deep(map); | |
return json_error_obj; | |
} | |
if(json_isspace(c)) c = json_readcharnospace(); | |
if(c != ':') | |
{ | |
var_delete_deep(key); | |
map_delete_deep(map); | |
return json_error("Invalid character found in an object."); | |
} | |
new String:test; | |
if(!var_get_safe(key, test)) | |
{ | |
var_delete_deep(key); | |
map_delete_deep(map); | |
return json_error("Object key is not a string."); | |
} | |
new GlobalVariant:value = json_readobj(c); | |
if(_:value == _:json_error_obj) | |
{ | |
var_delete_deep(key); | |
map_delete_deep(map); | |
return json_error_obj; | |
} | |
if(json_isspace(c)) c = json_readcharnospace(); | |
new bool:ok = map_var_add_var(map, key, value); | |
var_delete(key); | |
var_delete(value); | |
if(!ok) | |
{ | |
map_delete_deep(map); | |
return json_error("Duplicate key found."); | |
} | |
if(c == '}') break; | |
if(c != ',') | |
{ | |
map_delete_deep(map); | |
return json_error("Invalid character found in an object."); | |
} | |
} | |
c = json_ichar; | |
return var_to_global(var_new(map)); | |
} | |
case ']', '}': | |
{ | |
return VAR_NULL; | |
} | |
case 't': // true | |
{ | |
if(json_testread("rue")) | |
{ | |
c = json_ichar; | |
return var_to_global(var_new(true)); | |
}else{ | |
return json_error("Invalid token."); | |
} | |
} | |
case 'f': // false | |
{ | |
if(json_testread("alse")) | |
{ | |
c = json_ichar; | |
return var_to_global(var_new(false)); | |
}else{ | |
return json_error("Invalid token."); | |
} | |
} | |
case 'n': // null | |
{ | |
if(json_testread("ull")) | |
{ | |
c = json_ichar; | |
return VAR_NULL; | |
}else{ | |
return json_error("Invalid token."); | |
} | |
} | |
case '-', '0'..'9': //number | |
{ | |
new intnum; | |
new Float:floatnum; | |
new bool:neg = c == '-'; | |
if(!neg) | |
{ | |
floatnum = intnum = c - '0'; | |
} | |
new bool:isfloat = false; | |
for(;;) | |
{ | |
c = json_readchar(); | |
switch(c) | |
{ | |
case '0'..'9': | |
{ | |
floatnum = floatnum * 10.0 + float(c - '0'); | |
if(!isfloat) | |
{ | |
intnum = intnum * 10 + c - '0'; | |
if(intnum < 0) | |
{ | |
isfloat = true; | |
} | |
} | |
continue; | |
} | |
case '.': | |
{ | |
isfloat = true; | |
new Float:base = 0.1; | |
for(;;) | |
{ | |
c = json_readchar(); | |
switch(c) | |
{ | |
case '0'..'9': | |
{ | |
floatnum += base * float(c - '0'); | |
base /= 10.0; | |
continue; | |
} | |
} | |
break; | |
} | |
if(base == 0.1) | |
{ | |
return json_error("Invalid number."); | |
} | |
} | |
} | |
break; | |
} | |
if(neg) | |
{ | |
intnum *= -1; | |
floatnum *= -1.0; | |
} | |
if(isfloat) | |
{ | |
return var_to_global(var_new(floatnum)); | |
}else{ | |
return var_to_global(var_new(intnum)); | |
} | |
} | |
case '\"': //string | |
{ | |
new GlobalString:str = @(""); | |
new len = 0; | |
new bool:end = false; | |
for(; !end;) | |
{ | |
c = json_readchar(); | |
switch(c) | |
{ | |
case '\\': | |
{ | |
c = json_readchar(); | |
switch(c) | |
{ | |
case 'b': | |
c = '\b'; | |
case 't': | |
c = '\t'; | |
case 'n': | |
c = '\n'; | |
case 'f': | |
c = '\f'; | |
case 'r': | |
c = '\r'; | |
case '\"': | |
c = '\"'; | |
case 'u': | |
{ | |
c = 0; | |
for(new i = 0; i < 4; i++) | |
{ | |
new x = json_readchar(); | |
if('0' <= x <= '9') | |
{ | |
x -= '0'; | |
}else if('a' <= x <= 'f') | |
{ | |
x -= 'a' - 10; | |
}else if('A' <= x <= 'F') | |
{ | |
x -= 'A' - 10; | |
}else{ | |
str_delete(str); | |
return json_error("Invalid escape sequence."); | |
} | |
c = c * 16 + x; | |
} | |
} | |
default: | |
{ | |
str_delete(str); | |
return json_error("Invalid escape sequence."); | |
} | |
} | |
} | |
case '\"': | |
{ | |
end = true; | |
break; | |
} | |
case EOF: | |
{ | |
str_delete(str); | |
return json_error("Unexpected end of string."); | |
} | |
} | |
if(!end) | |
{ | |
len++; | |
str_resize(str, len, c); | |
} | |
} | |
c = json_ichar; | |
return var_to_global(var_new(str)); | |
} | |
} | |
return json_error("Invalid character."); | |
} | |
stock GlobalVariant:json_open(File:input) | |
{ | |
json_input = input; | |
new c; | |
new GlobalVariant:obj = json_readobj(c); | |
if(json_isspace(c)) c = json_readcharnospace(); | |
if(c != EOF) | |
{ | |
var_delete_deep(obj); | |
return json_error("End of file not reached."); | |
} | |
return obj; | |
} | |
//-------------------// | |
native print_s(AmxString:string) = print; | |
stock DumpObject(Variant:v) | |
{ | |
new List:l; | |
if(var_get_safe(v, l)) | |
{ | |
printf("["); | |
new size = list_size(l); | |
for(new i = 0; i < size; i++) | |
{ | |
DumpObject(list_get_var(l, i)); | |
} | |
printf("]"); | |
return; | |
} | |
new Map:m; | |
if(var_get_safe(v, m)) | |
{ | |
printf("{"); | |
new size = map_size(m); | |
for(new i = 0; i < size; i++) | |
{ | |
DumpObject(map_var_key_at(m, i)); | |
DumpObject(map_var_value_at(m, i)); | |
} | |
printf("}"); | |
return; | |
} | |
print_s(str_val(v)); | |
} | |
public OnFilterScriptInit() | |
{ | |
new File:f = fopen("test.json", io_read); | |
new GlobalVariant:v = json_open(f); | |
fclose(f); | |
if(_:v != _:json_error_obj) | |
{ | |
DumpObject(v); | |
var_delete_deep(v); | |
} | |
assert(pp_num_global_strings() == 0); | |
assert(pp_num_global_variants() == 0); | |
assert(pp_num_lists() == 0); | |
assert(pp_num_maps() == 0); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment