Skip to content

Commit

Permalink
Add a native JSON interface
Browse files Browse the repository at this point in the history
  • Loading branch information
sorear committed Dec 30, 2010
1 parent 97134d2 commit 049d8e5
Show file tree
Hide file tree
Showing 4 changed files with 164 additions and 20 deletions.
2 changes: 2 additions & 0 deletions lib/CLRBackend.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3057,6 +3057,8 @@ static NamProcessor() {
thandlers["iter_copy_elems"] = Methody(null, Tokens.Kernel.GetMethod("IterCopyElems"));
thandlers["to_jsync"] = Methody(null, typeof(JsyncWriter).GetMethod("ToJsync"));
thandlers["from_jsync"] = Methody(null, typeof(JsyncReader).GetMethod("FromJsync"));
thandlers["to_json"] = Methody(null, typeof(JsonWriter).GetMethod("ToJson"));
thandlers["from_json"] = Methody(null, typeof(JsyncReader).GetMethod("FromJson"));
thandlers["do_require"] = Methody(null, Tokens.Kernel.GetMethod("DoRequire"));
thandlers["frame_caller"] = FieldGet(Tokens.Frame, "caller");
thandlers["frame_file"] = Methody(null, Tokens.Frame.GetMethod("ExecutingFile"));
Expand Down
178 changes: 159 additions & 19 deletions lib/JSYNC.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
using System.Text;

public class JsyncWriter {
static bool FailSoft =
internal static bool FailSoft =
Environment.GetEnvironmentVariable("NIECZA_JSYNC_WRITER_FAILSOFT") != null;
StringBuilder o = new StringBuilder();
Dictionary<object,int> anchors = new Dictionary<object,int>();
Expand Down Expand Up @@ -130,44 +130,48 @@ void WriteStr(bool esc, string s) {
o.Append('"');
if (esc && NeedsEscape(s))
o.Append('.');
AddStringContents(o, s);
o.Append('"');
}

internal static void AddStringContents(StringBuilder sb, string s) {
foreach (char ch in s) {
switch(ch) {
case '\\':
o.Append('\\');
sb.Append('\\');
goto default;
case '\"':
o.Append('\\');
sb.Append('\\');
goto default;
default:
if ((ch & 0xFF7F) < 32) {
o.AppendFormat("\\u{0:X4}", (int)ch);
sb.AppendFormat("\\u{0:X4}", (int)ch);
} else {
o.Append(ch);
sb.Append(ch);
}
break;
case '\b':
o.Append('\\');
o.Append('b');
sb.Append('\\');
sb.Append('b');
break;
case '\f':
o.Append('\\');
o.Append('f');
sb.Append('\\');
sb.Append('f');
break;
case '\t':
o.Append('\\');
o.Append('t');
sb.Append('\\');
sb.Append('t');
break;
case '\r':
o.Append('\\');
o.Append('r');
sb.Append('\\');
sb.Append('r');
break;
case '\n':
o.Append('\\');
o.Append('n');
sb.Append('\\');
sb.Append('n');
break;
}
}
o.Append('"');
}

void WriteBool(bool x) {
Expand Down Expand Up @@ -329,6 +333,67 @@ string GetJsonString() {
}
}

Variable GetFromJson(bool top_level) {
char look = SkipWhite(true);
if (look == '[') {
VarDeque q = new VarDeque();
SkipChar('[');
while (true) {
look = SkipWhite(true);
if (look == ']')
break;
if (q.Count() != 0)
SkipChar(',');
q.Push(GetFromJson(false));
}
SkipWhite(true);
SkipChar(']');
IP6 i = new DynObject(Kernel.ArrayMO);
i.SetSlot("items", q);
i.SetSlot("rest", new VarDeque());
return Kernel.NewROScalar(i);
} else if (look == '{') {
VarHash q = new VarHash();
int ct = 0;
SkipChar('{');
while (true) {
look = SkipWhite(true);
if (look == '}')
break;
if (ct != 0)
SkipCharWS(',');
ct++;
string key = GetJsonString();
SkipWhite(true);
SkipChar(':');
q[key] = GetFromJson(false);
}
SkipWhite(true);
SkipChar('}');
return BoxRW<VarHash>(q, Kernel.HashMO);
} else if (top_level) {
Err("Top-level scalar found");
return null;
} else if (look == '"') {
return BoxRW<string>(GetJsonString(), Kernel.StrMO);
} else if (look == 'n') {
SkipToken("null");
return Kernel.NewRWScalar(Kernel.AnyMO, Kernel.AnyP);
} else if (look == 't') {
SkipToken("true");
return BoxRW<bool>(true, Kernel.BoolMO);
} else if (look == 'f') {
SkipToken("false");
return BoxRW<bool>(false, Kernel.BoolMO);
} else {
double d;
string tx = GetJsonNumber();
if (!double.TryParse(tx, out d))
Err("Unparsable number " + tx);
return BoxRW<double>(d, Kernel.NumMO);
}
}

void AddAnchor(string anch, IP6 obj) {
for (int i = 0; i < anch.Length; i++) {
char c = anch[i];
Expand Down Expand Up @@ -608,6 +673,12 @@ Variable GetFromHash() {
}

Variable GetFromNumber() {
s_content = GetJsonNumber();
s_tag = "Num";
return ParseScalar();
}

string GetJsonNumber() {
StringBuilder s = new StringBuilder();
do {
if (from.Length == ix)
Expand All @@ -619,9 +690,7 @@ Variable GetFromNumber() {
s.Append(x);
} while (true);
ix--;
s_content = s.ToString();
s_tag = "Num";
return ParseScalar();
return s.ToString();
}

int VersionComponent(string dgs) {
Expand Down Expand Up @@ -701,6 +770,18 @@ Variable GetTopLevel() {

// TODO GetTopLevel

public static IP6 FromJson(string inp) {
JsyncReader j = new JsyncReader();
j.from = inp;
j.SkipWhite(true);
Variable top = j.GetFromJson(true);
j.SkipWhite(false);
if (j.ix != inp.Length)
j.Err("Trailing garbage after object");

return top.Fetch();
}

public static IP6 FromJsync(string inp) {
JsyncReader j = new JsyncReader();
j.from = inp;
Expand All @@ -723,3 +804,62 @@ public static IP6 FromJsync(string inp) {
return top.Fetch();
}
}

public class JsonWriter {
StringBuilder o = new StringBuilder();

void WriteVal(IP6 obj) {
if (!obj.IsDefined()) {
o.Append("null");
} else if (obj.Isa(Kernel.BoolMO)) {
o.Append(Kernel.UnboxAny<bool>(obj) ? "true" : "false");
} else if (obj.Isa(Kernel.NumMO)) {
o.Append(Kernel.UnboxAny<double>(obj));
} else if (obj.Isa(Kernel.StrMO)) {
o.Append('"');
JsyncWriter.AddStringContents(o, Kernel.UnboxAny<string>(obj));
o.Append('"');
} else {
WriteObj(obj);
}
}

void WriteObj(IP6 obj) {
bool comma = false;
bool def = obj.IsDefined();
if (def && obj.Isa(Kernel.HashMO)) {
VarHash vh = Kernel.UnboxAny<VarHash>(obj);
o.Append('{');
foreach(KeyValuePair<string,Variable> kv in vh) {
if (comma) o.Append(',');
comma = true;
o.Append('"');
JsyncWriter.AddStringContents(o, kv.Key);
o.Append('"');
o.Append(':');
WriteVal(kv.Value.Fetch());
}
o.Append('}');
} else if (def && obj.Isa(Kernel.ListMO)) {
VarDeque iter = new VarDeque(Kernel.NewRWListVar(obj));
o.Append('[');
while (Kernel.IterHasFlat(iter, true)) {
if (comma) o.Append(',');
comma = true;
WriteVal(iter.Shift().Fetch());
}
o.Append(']');
} else if (JsyncWriter.FailSoft) {
o.Append("\"*UNSERIALIZABLE*\"");
} else {
throw new NieczaException("JSON writer encountered value of type " +
obj.mo.name);
}
}

public static string ToJson(IP6 obj) {
JsonWriter w = new JsonWriter();
w.WriteObj(obj);
return w.o.ToString();
}
}
2 changes: 2 additions & 0 deletions lib/JSYNC.pm6
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,5 @@ module JSYNC;

sub to-jsync($obj) is export { Q:CgOp { (box Str (to_jsync (@ {$obj}))) } }
sub from-jsync($obj) is export { Q:CgOp { (ns (from_jsync (unbox str (@ {$obj})))) } }
sub to-json($obj) is export { Q:CgOp { (box Str (to_json (@ {$obj}))) } }
sub from-json($obj) is export { Q:CgOp { (ns (from_json (unbox str (@ {$obj})))) } }
2 changes: 1 addition & 1 deletion src/CgOp.pm
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ BEGIN {
vvarlist_shift, vvarlist_sort, vvarlist_to_fvarlist,
vvarlist_unshift, vvarlist_unshiftn, whileloop,
get_lexer, run_protoregex, label_table, mrl_count, mrl_index,
treader_open, bif_make, cursor_ast,
treader_open, bif_make, cursor_ast, to_json, from_json,
>;

my $code;
Expand Down

0 comments on commit 049d8e5

Please sign in to comment.