Skip to content

Instantly share code, notes, and snippets.

@nadako
Created February 18, 2014 22:19
Show Gist options
  • Save nadako/9081608 to your computer and use it in GitHub Desktop.
Save nadako/9081608 to your computer and use it in GitHub Desktop.
Using haxe macros as syntax-tolerant, position-aware json parser
import haxe.macro.Context;
import haxe.macro.Expr;
using haxe.macro.ExprTools;
class Main
{
inline static var QUOTED_FIELD_PREFIX = "@$__hx__";
static function main()
{
var e = parseFile("test.json");
validateExpr(e);
trace(extractValue(e));
}
static function parseFile(path:String):Expr
{
var content = sys.io.File.getContent(path);
var pos = Context.makePosition({min: 0, max: 0, file: path});
return Context.parseInlineString(content, pos);
}
static function validateExpr(e:Expr):Void
{
switch (e.expr)
{
case EConst(CInt(_) | CFloat(_) | CString(_) | CIdent("true" | "false" | "null")): // constants
case EBlock([]): // empty object
case EObjectDecl(fields): for (f in fields) validateExpr(f.expr);
case EArrayDecl(exprs): for (e in exprs) validateExpr(e);
default:
throw new Error("Invalid JSON expression: " + e.toString(), e.pos);
}
}
static function extractValue(e:Expr):Dynamic
{
switch (e.expr)
{
case EConst(c):
switch (c)
{
case CInt(s):
var i = Std.parseInt(s);
return (i != null) ? i : Std.parseFloat(s); // if the number exceeds standard int return as float
case CFloat(s):
return Std.parseFloat(s);
case CString(s):
return s;
case CIdent("null"):
return null;
case CIdent("true"):
return true;
case CIdent("false"):
return false;
default:
}
case EBlock([]):
return {};
case EObjectDecl(fields):
var object = {};
for (field in fields)
Reflect.setField(object, unquoteField(field.field), extractValue(field.expr));
return object;
case EArrayDecl(exprs):
return [for (e in exprs) extractValue(e)];
default:
}
throw new Error("Invalid JSON expression: " + e.toString(), e.pos);
}
// see https://github.com/HaxeFoundation/haxe/issues/2642
static function unquoteField(name:String):String
{
return (name.indexOf(QUOTED_FIELD_PREFIX) == 0) ? name.substr(QUOTED_FIELD_PREFIX.length) : name;
}
}
/* some comment */
{
"a.b": {
key: // look at me, unquoted object key (also, line comment)
[
1,
"s",
null,
false,
true,
20.5
]
}
}
-cp src
--macro Main.main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment