It would be easy to write output with echo() that could be trivially
post-processed into JSON, for simple cases.
It would be a little harder to handle cases that require quoting in the
strings.
But what's harder is that OpenSCAD doesn't have the data structures
(yet) that correspond to JSON. It doesn't have a way for you to have a
name-value-list data structure that would naturally turn into a JSON
object. That means that you'd have to build your JSON "by hand", doing
your own nesting and whatnot. Or live with the limits of scalars and
arrays, with no objects.
That will change when we eventually add syntactic support for creating
objects; it will then be more or less straightforward to write a module
that would turn an OpenSCAD value into a series of echo() calls that
could then be trivially post-processed into proper JSON.
Of course, that leaves the long-discussed questions of (a) how to emit
text without echo's decorations, and (b) how to capture the desired
output, and only that output. But that's kind of orthogonal.
In fact, it's possible today to write a general "echo JSON" module that
will work with scalars, arrays, and objects. Not sure what to do with
ranges. If I remember tonight I might write it.
And then you post-process it with something like
sed -n 's/^ECHO: "\(.*\)"$/\1/p'
On 12/14/2021 3:46 PM, Jordan Brown wrote:
In fact, it's possible today to write a general "echo JSON" module
that will work with scalars, arrays, and objects. Not sure what to do
with ranges. If I remember tonight I might write it.
And here it is, a function that takes an OpenSCAD value (except
functions and ranges) and returns a JSON string. Lightly tested.
function json(v) =
is_undef(v) ? "null"
: is_bool(v) ? str(v)
: is_num(v) ? str(v)
: is_string(v) ? json_string(v)
: is_list(v) ? json_list(v)
: is_object(v) ? json_object(v)
: assert(false, str("Don't know how to turn \"", v, "\" into JSON"));
function json_string(s) =
let(a = [ for (c=s) json_char(c) ])
str("\"", join(a), "\"");
function json_char(c) =
c == "\"" ? "\\\""
: c == "\\" ? "\\\\"
: c;
function join(a, sep="", i=0) =
i >= len(a) ? ""
: str(i == 0 ? "" : sep, a[i], join(a, sep=sep, i=i+1));
function json_list(a) = str("[", join([ for (v=a) json(v) ], ","), "]");
function json_object(o) = str(
"{",
join([ for (k=o) str(json_string(k), "=", json(o[k])) ], ","),
"}"
);
echo(json([undef, true, 123,["aaa","bbb","\"\\", textmetrics("hello")]]));
which outputs:
ECHO:
"[null,true,123,["aaa","bbb","\"\\",{"position"=[0.96,-0.1408],"size"=[27.8024,10.208],"ascent"=10.0672,"descent"=-0.1408,"offset"=[0,0],"advance"=[29.3443,0]}]]"
Or, reformatted for readability:
ECHO: " [ null, true, 123, [ "aaa", "bbb", "\"\\", {
"position"=[0.96,-0.1408], "size"=[27.8024,10.208],
"ascent"=10.0672, "descent"=-0.1408, "offset"=[0,0],
"advance"=[29.3443,0] } ] ] "
Notes:
And then you post-process it with something like
sed -n 's/^ECHO: "\(.*\)"$/\1/p'
Oops, got my languages confused; the "=" in objects should be a ":".
function json(v) =
is_undef(v) ? "null"
: is_bool(v) ? str(v)
: is_num(v) ? str(v)
: is_string(v) ? json_string(v)
: is_list(v) ? json_list(v)
: is_object(v) ? json_object(v)
: assert(false, str("Don't know how to turn \"", v, "\" into JSON"));
function json_string(s) =
let(a = [ for (c=s) json_char(c) ])
str("\"", join(a), "\"");
function json_char(c) =
c == "\"" ? "\\\""
: c == "\\" ? "\\\\"
: c;
function join(a, sep="", i=0) =
i >= len(a) ? ""
: str(i == 0 ? "" : sep, a[i], join(a, sep=sep, i=i+1));
function json_list(a) = str("[", join([ for (v=a) json(v) ], ","), "]");
function json_object(o) = str(
"{",
join([ for (k=o) str(json_string(k), ":", json(o[k])) ], ","),
"}"
);
echo(json([undef, true, 123,["aaa","bbb","\"\\", textmetrics("hello")]]));