[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