[njs] Added AST serialization.
Dmitry Volyntsev
xeioex at nginx.com
Fri May 29 19:30:59 UTC 2020
details: https://hg.nginx.org/njs/rev/d255e73aed3b
branches:
changeset: 1408:d255e73aed3b
user: Dmitry Volyntsev <xeioex at nginx.com>
date: Fri May 29 19:29:59 2020 +0000
description:
Added AST serialization.
diffstat:
src/njs.h | 2 +
src/njs_parser.c | 256 +++++++++++++++++++++++++++++++++++++++++++++++
src/njs_parser.h | 2 +
src/njs_shell.c | 7 +
src/njs_vm.c | 21 +++-
test/njs_expect_test.exp | 4 +
6 files changed, 291 insertions(+), 1 deletions(-)
diffs (391 lines):
diff -r 86f55a7dc4a4 -r d255e73aed3b src/njs.h
--- a/src/njs.h Fri May 29 20:00:32 2020 +0300
+++ b/src/njs.h Fri May 29 19:29:59 2020 +0000
@@ -206,6 +206,7 @@ typedef struct {
* unsafe - enables unsafe language features:
* - Function constructors.
* module - ES6 "module" mode. Script mode is default.
+ * ast - print AST.
*/
uint8_t trailer; /* 1 bit */
@@ -217,6 +218,7 @@ typedef struct {
uint8_t sandbox; /* 1 bit */
uint8_t unsafe; /* 1 bit */
uint8_t module; /* 1 bit */
+ uint8_t ast; /* 1 bit */
} njs_vm_opt_t;
diff -r 86f55a7dc4a4 -r d255e73aed3b src/njs_parser.c
--- a/src/njs_parser.c Fri May 29 20:00:32 2020 +0300
+++ b/src/njs_parser.c Fri May 29 19:29:59 2020 +0000
@@ -449,6 +449,11 @@ static njs_token_type_t njs_parser_escap
static njs_int_t njs_parser_escape_string_calc_length(njs_parser_t *parser,
njs_lexer_token_t *token, size_t *out_size, size_t *out_length);
+static void njs_parser_serialize_tree(njs_chb_t *chain,
+ njs_parser_node_t *node, njs_int_t *ret, size_t indent);
+static njs_int_t njs_parser_serialize_node(njs_chb_t *chain,
+ njs_parser_node_t *node);
+
#define njs_parser_chain_top(parser) \
((parser)->scope->top)
@@ -8269,3 +8274,254 @@ njs_parser_node_error(njs_vm_t *vm, njs_
njs_parser_scope_error(vm, node->scope, type, node->token_line, fmt, args);
va_end(args);
}
+
+
+njs_int_t
+njs_parser_serialize_ast(njs_parser_node_t *node, njs_chb_t *chain)
+{
+ njs_int_t ret;
+
+ ret = NJS_OK;
+
+ njs_parser_serialize_tree(chain, node, &ret, 0);
+ njs_chb_append_literal(chain, "\n");
+
+ return ret;
+}
+
+
+njs_inline void
+njs_parser_serialize_indent(njs_chb_t *chain, size_t indent)
+{
+ size_t i;
+
+ for (i = 0; i < indent; i++) {
+ njs_chb_append_literal(chain, " ");
+ }
+}
+
+
+static void
+njs_parser_serialize_tree(njs_chb_t *chain, njs_parser_node_t *node,
+ njs_int_t *ret, size_t indent)
+{
+ njs_str_t str;
+
+ njs_chb_append_literal(chain, "{\"name\": \"");
+
+ *ret |= njs_parser_serialize_node(chain, node);
+
+ njs_chb_append_literal(chain, "\"");
+
+ switch (node->token_type) {
+ case NJS_TOKEN_NUMBER:
+ case NJS_TOKEN_STRING:
+ case NJS_TOKEN_NAME:
+ case NJS_TOKEN_FUNCTION_CALL:
+ njs_chb_append_literal(chain, ",\n");
+ njs_parser_serialize_indent(chain, indent);
+ njs_chb_sprintf(chain, 32, " \"index\": \"%p\"", node->index);
+
+ switch (node->token_type) {
+ case NJS_TOKEN_NUMBER:
+ case NJS_TOKEN_STRING:
+ njs_chb_append_literal(chain, ",\n");
+ njs_parser_serialize_indent(chain, indent);
+
+ if (node->token_type == NJS_TOKEN_NUMBER) {
+ njs_chb_sprintf(chain, 32, " \"value\": %f\n",
+ njs_number(&node->u.value));
+
+ } else {
+ njs_string_get(&node->u.value, &str);
+ njs_chb_sprintf(chain, 32, " \"value\": \"%V\"\n", &str);
+ }
+
+ break;
+
+ default:
+ break;
+ }
+
+ break;
+
+ default:
+ break;
+ }
+
+ if (node->left != NULL) {
+ njs_chb_append_literal(chain, ",\n");
+ njs_parser_serialize_indent(chain, indent);
+ njs_chb_append_literal(chain, " \"left\": ");
+
+ njs_parser_serialize_tree(chain, node->left, ret, indent + 1);
+ }
+
+ if (node->right != NULL) {
+ njs_chb_append_literal(chain, ",\n");
+ njs_parser_serialize_indent(chain, indent);
+ njs_chb_append_literal(chain, " \"right\": ");
+
+ njs_parser_serialize_tree(chain, node->right, ret, indent + 1);
+ }
+
+ njs_chb_append_literal(chain, "}");
+}
+
+
+static njs_int_t
+njs_parser_serialize_node(njs_chb_t *chain, njs_parser_node_t *node)
+{
+ const char *name;
+
+#define njs_token_serialize(token) \
+ case token: \
+ name = &njs_stringify(token)[njs_length("NJS_TOKEN_")]; \
+ njs_chb_append(chain, name, njs_strlen(name)); \
+ break
+
+ switch (node->token_type) {
+ njs_token_serialize(NJS_TOKEN_END);
+ /* FIXME: NJS_TOKEN_ILLEGAL should not be present in AST */
+ njs_token_serialize(NJS_TOKEN_ILLEGAL);
+ njs_token_serialize(NJS_TOKEN_COMMA);
+ njs_token_serialize(NJS_TOKEN_CONDITIONAL);
+ njs_token_serialize(NJS_TOKEN_ASSIGNMENT);
+ njs_token_serialize(NJS_TOKEN_ADDITION_ASSIGNMENT);
+ njs_token_serialize(NJS_TOKEN_SUBSTRACTION_ASSIGNMENT);
+ njs_token_serialize(NJS_TOKEN_MULTIPLICATION_ASSIGNMENT);
+ njs_token_serialize(NJS_TOKEN_EXPONENTIATION_ASSIGNMENT);
+ njs_token_serialize(NJS_TOKEN_DIVISION_ASSIGNMENT);
+ njs_token_serialize(NJS_TOKEN_REMAINDER_ASSIGNMENT);
+ njs_token_serialize(NJS_TOKEN_LEFT_SHIFT_ASSIGNMENT);
+ njs_token_serialize(NJS_TOKEN_RIGHT_SHIFT_ASSIGNMENT);
+ njs_token_serialize(NJS_TOKEN_UNSIGNED_RIGHT_SHIFT_ASSIGNMENT);
+ njs_token_serialize(NJS_TOKEN_BITWISE_OR_ASSIGNMENT);
+ njs_token_serialize(NJS_TOKEN_BITWISE_XOR_ASSIGNMENT);
+ njs_token_serialize(NJS_TOKEN_BITWISE_AND_ASSIGNMENT);
+ njs_token_serialize(NJS_TOKEN_EQUAL);
+ njs_token_serialize(NJS_TOKEN_NOT_EQUAL);
+ njs_token_serialize(NJS_TOKEN_STRICT_EQUAL);
+ njs_token_serialize(NJS_TOKEN_STRICT_NOT_EQUAL);
+ njs_token_serialize(NJS_TOKEN_ADDITION);
+ njs_token_serialize(NJS_TOKEN_UNARY_PLUS);
+ njs_token_serialize(NJS_TOKEN_INCREMENT);
+ njs_token_serialize(NJS_TOKEN_POST_INCREMENT);
+ njs_token_serialize(NJS_TOKEN_SUBSTRACTION);
+ njs_token_serialize(NJS_TOKEN_UNARY_NEGATION);
+ njs_token_serialize(NJS_TOKEN_DECREMENT);
+ njs_token_serialize(NJS_TOKEN_POST_DECREMENT);
+ njs_token_serialize(NJS_TOKEN_MULTIPLICATION);
+ njs_token_serialize(NJS_TOKEN_EXPONENTIATION);
+ njs_token_serialize(NJS_TOKEN_DIVISION);
+ njs_token_serialize(NJS_TOKEN_REMAINDER);
+ njs_token_serialize(NJS_TOKEN_LESS);
+ njs_token_serialize(NJS_TOKEN_LESS_OR_EQUAL);
+ njs_token_serialize(NJS_TOKEN_LEFT_SHIFT);
+ njs_token_serialize(NJS_TOKEN_GREATER);
+ njs_token_serialize(NJS_TOKEN_GREATER_OR_EQUAL);
+ njs_token_serialize(NJS_TOKEN_RIGHT_SHIFT);
+ njs_token_serialize(NJS_TOKEN_UNSIGNED_RIGHT_SHIFT);
+ njs_token_serialize(NJS_TOKEN_BITWISE_OR);
+ njs_token_serialize(NJS_TOKEN_LOGICAL_OR);
+ njs_token_serialize(NJS_TOKEN_BITWISE_XOR);
+ njs_token_serialize(NJS_TOKEN_BITWISE_AND);
+ njs_token_serialize(NJS_TOKEN_LOGICAL_AND);
+ njs_token_serialize(NJS_TOKEN_BITWISE_NOT);
+ njs_token_serialize(NJS_TOKEN_LOGICAL_NOT);
+ njs_token_serialize(NJS_TOKEN_COALESCE);
+ njs_token_serialize(NJS_TOKEN_IN);
+ njs_token_serialize(NJS_TOKEN_OF);
+ njs_token_serialize(NJS_TOKEN_INSTANCEOF);
+ njs_token_serialize(NJS_TOKEN_TYPEOF);
+ njs_token_serialize(NJS_TOKEN_VOID);
+ njs_token_serialize(NJS_TOKEN_NEW);
+ njs_token_serialize(NJS_TOKEN_DELETE);
+ njs_token_serialize(NJS_TOKEN_YIELD);
+
+ njs_token_serialize(NJS_TOKEN_NULL);
+ njs_token_serialize(NJS_TOKEN_NUMBER);
+ njs_token_serialize(NJS_TOKEN_TRUE);
+ njs_token_serialize(NJS_TOKEN_FALSE);
+ njs_token_serialize(NJS_TOKEN_STRING);
+ njs_token_serialize(NJS_TOKEN_TEMPLATE_LITERAL);
+ njs_token_serialize(NJS_TOKEN_NAME);
+ njs_token_serialize(NJS_TOKEN_OBJECT);
+ njs_token_serialize(NJS_TOKEN_OBJECT_VALUE);
+ njs_token_serialize(NJS_TOKEN_ARRAY);
+ njs_token_serialize(NJS_TOKEN_REGEXP);
+
+ njs_token_serialize(NJS_TOKEN_PROPERTY);
+ njs_token_serialize(NJS_TOKEN_PROPERTY_INIT);
+ njs_token_serialize(NJS_TOKEN_PROPERTY_DELETE);
+ njs_token_serialize(NJS_TOKEN_PROPERTY_GETTER);
+ njs_token_serialize(NJS_TOKEN_PROPERTY_SETTER);
+
+ njs_token_serialize(NJS_TOKEN_PROTO_INIT);
+
+ njs_token_serialize(NJS_TOKEN_FUNCTION);
+ njs_token_serialize(NJS_TOKEN_FUNCTION_EXPRESSION);
+ njs_token_serialize(NJS_TOKEN_FUNCTION_CALL);
+ njs_token_serialize(NJS_TOKEN_METHOD_CALL);
+
+ njs_token_serialize(NJS_TOKEN_ARGUMENT);
+ njs_token_serialize(NJS_TOKEN_RETURN);
+ njs_token_serialize(NJS_TOKEN_STATEMENT);
+ njs_token_serialize(NJS_TOKEN_BLOCK);
+ njs_token_serialize(NJS_TOKEN_VAR);
+ njs_token_serialize(NJS_TOKEN_LET);
+ njs_token_serialize(NJS_TOKEN_IF);
+ njs_token_serialize(NJS_TOKEN_ELSE);
+ njs_token_serialize(NJS_TOKEN_BRANCHING);
+ njs_token_serialize(NJS_TOKEN_WHILE);
+ njs_token_serialize(NJS_TOKEN_DO);
+ njs_token_serialize(NJS_TOKEN_FOR);
+ njs_token_serialize(NJS_TOKEN_FOR_IN);
+ njs_token_serialize(NJS_TOKEN_BREAK);
+ njs_token_serialize(NJS_TOKEN_CONTINUE);
+ njs_token_serialize(NJS_TOKEN_SWITCH);
+ njs_token_serialize(NJS_TOKEN_CASE);
+ njs_token_serialize(NJS_TOKEN_DEFAULT);
+ njs_token_serialize(NJS_TOKEN_WITH);
+ njs_token_serialize(NJS_TOKEN_TRY);
+ njs_token_serialize(NJS_TOKEN_CATCH);
+ njs_token_serialize(NJS_TOKEN_FINALLY);
+ njs_token_serialize(NJS_TOKEN_THROW);
+ njs_token_serialize(NJS_TOKEN_THIS);
+ njs_token_serialize(NJS_TOKEN_GLOBAL_OBJECT);
+ njs_token_serialize(NJS_TOKEN_NON_LOCAL_THIS);
+ njs_token_serialize(NJS_TOKEN_ARGUMENTS);
+ njs_token_serialize(NJS_TOKEN_EVAL);
+ njs_token_serialize(NJS_TOKEN_IMPORT);
+ njs_token_serialize(NJS_TOKEN_EXPORT);
+
+#if 0
+
+ njs_token_serialize(NJS_TOKEN_TARGET);
+ njs_token_serialize(NJS_TOKEN_META);
+ njs_token_serialize(NJS_TOKEN_ASYNC);
+ njs_token_serialize(NJS_TOKEN_AWAIT);
+ njs_token_serialize(NJS_TOKEN_CONST);
+ njs_token_serialize(NJS_TOKEN_DEBUGGER);
+ njs_token_serialize(NJS_TOKEN_ENUM);
+
+ njs_token_serialize(NJS_TOKEN_CLASS);
+ njs_token_serialize(NJS_TOKEN_EXTENDS);
+ njs_token_serialize(NJS_TOKEN_IMPLEMENTS);
+ njs_token_serialize(NJS_TOKEN_INTERFACE);
+ njs_token_serialize(NJS_TOKEN_PACKAGE);
+ njs_token_serialize(NJS_TOKEN_PRIVATE);
+ njs_token_serialize(NJS_TOKEN_PROTECTED);
+ njs_token_serialize(NJS_TOKEN_PUBLIC);
+ njs_token_serialize(NJS_TOKEN_STATIC);
+ njs_token_serialize(NJS_TOKEN_SUPER);
+
+#endif
+
+ default:
+ njs_chb_sprintf(chain, 32, "#UNDEF(%d)", (int) node->token_type);
+ return NJS_DECLINED;
+ }
+
+ return NJS_OK;
+}
diff -r 86f55a7dc4a4 -r d255e73aed3b src/njs_parser.h
--- a/src/njs_parser.h Fri May 29 20:00:32 2020 +0300
+++ b/src/njs_parser.h Fri May 29 19:29:59 2020 +0000
@@ -128,6 +128,8 @@ void njs_parser_lexer_error(njs_parser_t
void njs_parser_node_error(njs_vm_t *vm, njs_parser_node_t *node,
njs_object_type_t type, const char *fmt, ...);
+njs_int_t njs_parser_serialize_ast(njs_parser_node_t *node, njs_chb_t *chain);
+
#define njs_parser_restricted_identifier(token) \
(token == NJS_TOKEN_ARGUMENTS || token == NJS_TOKEN_EVAL)
diff -r 86f55a7dc4a4 -r d255e73aed3b src/njs_shell.c
--- a/src/njs_shell.c Fri May 29 20:00:32 2020 +0300
+++ b/src/njs_shell.c Fri May 29 19:29:59 2020 +0000
@@ -34,6 +34,7 @@ typedef struct {
uint8_t sandbox;
uint8_t safe;
uint8_t version;
+ uint8_t ast;
char *file;
char *command;
@@ -268,6 +269,7 @@ main(int argc, char **argv)
vm_options.external = &njs_console;
vm_options.argv = opts.argv;
vm_options.argc = opts.argc;
+ vm_options.ast = opts.ast;
if (opts.interactive) {
ret = njs_interactive_shell(&opts, &vm_options);
@@ -307,6 +309,7 @@ njs_get_options(njs_opts_t *opts, int ar
"njs [options] [-c string | script.js | -] [script args]"
"\n"
"Options:\n"
+ " -a print AST.\n"
" -c specify the command to execute.\n"
" -d print disassembled code.\n"
" -f disabled denormals mode.\n"
@@ -340,6 +343,10 @@ njs_get_options(njs_opts_t *opts, int ar
(void) write(STDOUT_FILENO, help, njs_length(help));
return ret;
+ case 'a':
+ opts->ast = 1;
+ break;
+
case 'c':
opts->interactive = 0;
diff -r 86f55a7dc4a4 -r d255e73aed3b src/njs_vm.c
--- a/src/njs_vm.c Fri May 29 20:00:32 2020 +0300
+++ b/src/njs_vm.c Fri May 29 19:29:59 2020 +0000
@@ -125,6 +125,8 @@ njs_int_t
njs_vm_compile(njs_vm_t *vm, u_char **start, u_char *end)
{
njs_int_t ret;
+ njs_str_t ast;
+ njs_chb_t chain;
njs_lexer_t lexer;
njs_parser_t *parser, *prev;
njs_generator_t generator;
@@ -202,7 +204,24 @@ njs_vm_compile(njs_vm_t *vm, u_char **st
njs_disassembler(vm);
}
- return NJS_OK;
+ if (njs_slow_path(vm->options.ast)) {
+ njs_chb_init(&chain, vm->mem_pool);
+ ret = njs_parser_serialize_ast(parser->node, &chain);
+ if (njs_slow_path(ret == NJS_ERROR)) {
+ return ret;
+ }
+
+ if (njs_slow_path(njs_chb_join(&chain, &ast) != NJS_OK)) {
+ return NJS_ERROR;
+ }
+
+ njs_print(ast.start, ast.length);
+
+ njs_chb_destroy(&chain);
+ njs_mp_free(vm->mem_pool, ast.start);
+ }
+
+ return ret;
fail:
diff -r 86f55a7dc4a4 -r d255e73aed3b test/njs_expect_test.exp
--- a/test/njs_expect_test.exp Fri May 29 20:00:32 2020 +0300
+++ b/test/njs_expect_test.exp Fri May 29 19:29:59 2020 +0000
@@ -791,6 +791,10 @@ njs_run {"-p" "test/module" "./test/modu
njs_run {"-h"} "Options"
+# ast
+
+njs_run {"-a" "-c" "console.log(1*2)"} "{\"name\": \"END\""
+
# command
njs_run {"-c" "console.log(\"a b c\")"} "a b c"
More information about the nginx-devel
mailing list