shithub: libmujs

Download patch

ref: 603977ae5bad97d544725cd6c36f9af30e6aef4e
parent: ffe0ca7d7f47dc11d8912b11daa388e66be358bb
author: Tor Andersson <tor.andersson@artifex.com>
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"