Skip to content

Instantly share code, notes, and snippets.

@IS4Code
Created May 9, 2018 14:33
Show Gist options
  • Save IS4Code/da010608d20909d6eba2eeed9d27c3df to your computer and use it in GitHub Desktop.
Save IS4Code/da010608d20909d6eba2eeed9d27c3df to your computer and use it in GitHub Desktop.
JSON parser example using PawnPlus
#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