ref: 603977ae5bad97d544725cd6c36f9af30e6aef4e
parent: ffe0ca7d7f47dc11d8912b11daa388e66be358bb
author: Tor Andersson <[email protected]>
date: Fri Mar 8 07:55:06 EST 2019
Add repr() function to shell, and use it in the REPL.
--- a/jsfunction.c
+++ b/jsfunction.c
@@ -71,7 +71,7 @@
if (i > 0) js_putc(J, &sb, ',');
js_puts(J, &sb, F->vartab[i]);
}
- js_puts(J, &sb, ") { ... }");
+ js_puts(J, &sb, ") { [byte code] }");
js_putc(J, &sb, 0);
js_pushstring(J, sb->s);
@@ -85,7 +85,7 @@
js_puts(J, &sb, "function ");
js_puts(J, &sb, self->u.c.name);
- js_puts(J, &sb, "() { ... }");
+ js_puts(J, &sb, "() { [native code] }");
js_putc(J, &sb, 0);
js_pushstring(J, sb->s);
@@ -92,7 +92,7 @@
js_endtry(J);
js_free(J, sb);
} else {
- js_pushliteral(J, "function () { ... }");
+ js_pushliteral(J, "function () { }");
}
}
--- a/jslex.c
+++ b/jslex.c
@@ -117,9 +117,15 @@
return c == 0xA || c == 0xD || c == 0x2028 || c == 0x2029;
}
+#ifndef isalpha
#define isalpha(c) ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'))
+#endif
+#ifndef isdigit
#define isdigit(c) (c >= '0' && c <= '9')
+#endif
+#ifndef ishex
#define ishex(c) ((c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'))
+#endif
static int jsY_isidentifierstart(int c)
{
--- /dev/null
+++ b/jsrepr.c
@@ -1,0 +1,270 @@
+#include "jsi.h"
+#include "jslex.h"
+#include "jsvalue.h"
+#include "jsbuiltin.h"
+
+static void reprvalue(js_State *J, js_Buffer **sb);
+
+static void reprnum(js_State *J, js_Buffer **sb, double n)
+{
+ char buf[40];
+ if (n == 0 && signbit(n))
+ js_puts(J, sb, "-0");
+ else
+ js_puts(J, sb, jsV_numbertostring(J, buf, n));
+}
+
+static void reprstr(js_State *J, js_Buffer **sb, const char *s)
+{
+ static const char *HEX = "0123456789ABCDEF";
+ Rune c;
+ js_putc(J, sb, '"');
+ while (*s) {
+ s += chartorune(&c, s);
+ switch (c) {
+ case '"': js_puts(J, sb, "\\\""); break;
+ case '\\': js_puts(J, sb, "\\\\"); break;
+ case '\b': js_puts(J, sb, "\\b"); break;
+ case '\f': js_puts(J, sb, "\\f"); break;
+ case '\n': js_puts(J, sb, "\\n"); break;
+ case '\r': js_puts(J, sb, "\\r"); break;
+ case '\t': js_puts(J, sb, "\\t"); break;
+ default:
+ if (c < ' ' || c > 127) {
+ js_puts(J, sb, "\\u");
+ js_putc(J, sb, HEX[(c>>12)&15]);
+ js_putc(J, sb, HEX[(c>>8)&15]);
+ js_putc(J, sb, HEX[(c>>4)&15]);
+ js_putc(J, sb, HEX[c&15]);
+ } else {
+ js_putc(J, sb, c); break;
+ }
+ }
+ }
+ js_putc(J, sb, '"');
+}
+
+#ifndef isalpha
+#define isalpha(c) ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'))
+#endif
+#ifndef isdigit
+#define isdigit(c) (c >= '0' && c <= '9')
+#endif
+
+static void reprident(js_State *J, js_Buffer **sb, const char *name)
+{
+ const char *p = name;
+ if (isdigit(*p))
+ while (isdigit(*p))
+ ++p;
+ else if (isalpha(*p) || *p == '_')
+ while (isdigit(*p) || isalpha(*p) || *p == '_')
+ ++p;
+ if (p > name && *p == 0)
+ js_puts(J, sb, name);
+ else
+ reprstr(J, sb, name);
+}
+
+static void reprobject(js_State *J, js_Buffer **sb)
+{
+ const char *key;
+ int i, n;
+
+ n = js_gettop(J) - 1;
+ for (i = 0; i < n; ++i) {
+ if (js_isobject(J, i)) {
+ if (js_toobject(J, i) == js_toobject(J, -1)) {
+ js_puts(J, sb, "{}");
+ return;
+ }
+ }
+ }
+
+ n = 0;
+ js_putc(J, sb, '{');
+ js_pushiterator(J, -1, 1);
+ while ((key = js_nextiterator(J, -1))) {
+ if (n++ > 0)
+ js_puts(J, sb, ", ");
+ reprident(J, sb, key);
+ js_puts(J, sb, ": ");
+ js_getproperty(J, -2, key);
+ reprvalue(J, sb);
+ js_pop(J, 1);
+ }
+ js_pop(J, 1);
+ js_putc(J, sb, '}');
+}
+
+static void reprarray(js_State *J, js_Buffer **sb)
+{
+ int n, i;
+
+ n = js_gettop(J) - 1;
+ for (i = 0; i < n; ++i) {
+ if (js_isobject(J, i)) {
+ if (js_toobject(J, i) == js_toobject(J, -1)) {
+ js_puts(J, sb, "[]");
+ return;
+ }
+ }
+ }
+
+ js_putc(J, sb, '[');
+ n = js_getlength(J, -1);
+ for (i = 0; i < n; ++i) {
+ if (i > 0)
+ js_puts(J, sb, ", ");
+ js_getindex(J, -1, i);
+ reprvalue(J, sb);
+ js_pop(J, 1);
+ }
+ js_putc(J, sb, ']');
+}
+
+static void reprfun(js_State *J, js_Buffer **sb, js_Function *fun)
+{
+ int i;
+ js_puts(J, sb, "function ");
+ js_puts(J, sb, fun->name);
+ js_putc(J, sb, '(');
+ for (i = 0; i < fun->numparams; ++i) {
+ if (i > 0)
+ js_puts(J, sb, ", ");
+ js_puts(J, sb, fun->vartab[i]);
+ }
+ js_puts(J, sb, ") { [byte code] }");
+}
+
+static void reprvalue(js_State *J, js_Buffer **sb)
+{
+ if (js_isundefined(J, -1))
+ js_puts(J, sb, "undefined");
+ else if (js_isnull(J, -1))
+ js_puts(J, sb, "null");
+ else if (js_isboolean(J, -1))
+ js_puts(J, sb, js_toboolean(J, -1) ? "true" : "false");
+ else if (js_isnumber(J, -1))
+ reprnum(J, sb, js_tonumber(J, -1));
+ else if (js_isstring(J, -1))
+ reprstr(J, sb, js_tostring(J, -1));
+ else if (js_isobject(J, -1)) {
+ js_Object *obj = js_toobject(J, -1);
+ switch (obj->type) {
+ default:
+ reprobject(J, sb);
+ break;
+ case JS_CARRAY:
+ reprarray(J, sb);
+ break;
+ case JS_CFUNCTION:
+ case JS_CSCRIPT:
+ reprfun(J, sb, obj->u.f.function);
+ break;
+ case JS_CCFUNCTION:
+ js_puts(J, sb, "function ");
+ js_puts(J, sb, obj->u.c.name);
+ js_puts(J, sb, "() { [native code] }");
+ break;
+ case JS_CBOOLEAN:
+ js_puts(J, sb, "(new Boolean(");
+ js_puts(J, sb, obj->u.boolean ? "true" : "false");
+ js_puts(J, sb, "))");
+ break;
+ case JS_CNUMBER:
+ js_puts(J, sb, "(new Number(");
+ reprnum(J, sb, obj->u.number);
+ js_puts(J, sb, "))");
+ break;
+ case JS_CSTRING:
+ js_puts(J, sb, "(new String(");
+ reprstr(J, sb, obj->u.s.string);
+ js_puts(J, sb, "))");
+ break;
+ case JS_CREGEXP:
+ js_putc(J, sb, '/');
+ js_puts(J, sb, obj->u.r.source);
+ js_putc(J, sb, '/');
+ if (obj->u.r.flags & JS_REGEXP_G) js_putc(J, sb, 'g');
+ if (obj->u.r.flags & JS_REGEXP_I) js_putc(J, sb, 'i');
+ if (obj->u.r.flags & JS_REGEXP_M) js_putc(J, sb, 'm');
+ break;
+ case JS_CDATE:
+ js_puts(J, sb, "(new Date(");
+ fmtnum(J, sb, obj->u.number);
+ js_puts(J, sb, "))");
+ break;
+ case JS_CERROR:
+ js_puts(J, sb, "(new ");
+ js_getproperty(J, -1, "name");
+ js_puts(J, sb, js_tostring(J, -1));
+ js_pop(J, 1);
+ js_putc(J, sb, '(');
+ js_getproperty(J, -1, "message");
+ reprstr(J, sb, js_tostring(J, -1));
+ js_pop(J, 1);
+ js_puts(J, sb, "))");
+ break;
+ case JS_CMATH:
+ js_puts(J, sb, "Math");
+ break;
+ case JS_CJSON:
+ js_puts(J, sb, "JSON");
+ break;
+ case JS_CITERATOR:
+ js_puts(J, sb, "[iterator ");
+ break;
+ case JS_CUSERDATA:
+ js_puts(J, sb, "[userdata ");
+ js_puts(J, sb, obj->u.user.tag);
+ js_putc(J, sb, ']');
+ break;
+ }
+ }
+}
+
+void js_repr(js_State *J, int idx)
+{
+ js_Buffer *sb = NULL;
+ int savebot;
+
+ if (js_try(J)) {
+ js_free(J, sb);
+ js_throw(J);
+ }
+
+ js_copy(J, idx);
+
+ savebot = J->bot;
+ J->bot = J->top - 1;
+ reprvalue(J, &sb);
+ J->bot = savebot;
+
+ js_pop(J, 1);
+
+ js_putc(J, &sb, 0);
+ js_pushstring(J, sb ? sb->s : "undefined");
+
+ js_endtry(J);
+ js_free(J, sb);
+}
+
+const char *js_torepr(js_State *J, int idx)
+{
+ js_repr(J, idx);
+ js_replace(J, idx < 0 ? idx-1 : idx);
+ return js_tostring(J, idx);
+}
+
+const char *js_tryrepr(js_State *J, int idx, const char *error)
+{
+ const char *s;
+ if (js_try(J)) {
+ js_pop(J, 1);
+ return error;
+ }
+ s = js_torepr(J, idx);
+ js_endtry(J);
+ return s;
+}
--- a/main.c
+++ b/main.c
@@ -199,6 +199,11 @@
exit(js_tonumber(J, 1));
}
+static void jsB_repr(js_State *J)
+{
+ js_repr(J, 1);
+}
+
static const char *require_js =
"function require(name) {\n"
"var cache = require.cache;\n"
@@ -220,7 +225,7 @@
static int eval_print(js_State *J, const char *source)
{
- if (js_ploadstring(J, "[string]", source)) {
+ if (js_ploadstring(J, "[stdin]", source)) {
fprintf(stderr, "%s\n", js_trystring(J, -1, "Error"));
js_pop(J, 1);
return 1;
@@ -231,8 +236,9 @@
js_pop(J, 1);
return 1;
}
- if (js_isdefined(J, -1))
- printf("%s\n", js_trystring(J, -1, "can't convert to string"));
+ if (js_isdefined(J, -1)) {
+ printf("%s\n", js_tryrepr(J, -1, "can't convert to string"));
+ }
js_pop(J, 1);
return 0;
}
@@ -315,6 +321,9 @@
js_newcfunction(J, jsB_readline, "readline", 0);
js_setglobal(J, "readline");
+
+ js_newcfunction(J, jsB_repr, "repr", 0);
+ js_setglobal(J, "repr");
js_newcfunction(J, jsB_quit, "quit", 1);
js_setglobal(J, "quit");
--- a/mujs.h
+++ b/mujs.h
@@ -215,6 +215,10 @@
int js_instanceof(js_State *J);
const char *js_typeof(js_State *J, int idx);
+void js_repr(js_State *J, int idx);
+const char *js_torepr(js_State *J, int idx);
+const char *js_tryrepr(js_State *J, int idx, const char *error);
+
#ifdef __cplusplus
}
#endif
--- a/one.c
+++ b/one.c
@@ -17,6 +17,7 @@
#include "jsparse.c"
#include "jsproperty.c"
#include "jsregexp.c"
+#include "jsrepr.c"
#include "jsrun.c"
#include "jsstate.c"
#include "jsstring.c"