diff --git a/Units/parser-javascript.r/self-invoking-anon-func.d/args.ctags b/Units/parser-javascript.r/self-invoking-anon-func.d/args.ctags new file mode 100644 index 0000000000..5ee5f79f70 --- /dev/null +++ b/Units/parser-javascript.r/self-invoking-anon-func.d/args.ctags @@ -0,0 +1 @@ +--sort=no diff --git a/Units/parser-javascript.r/self-invoking-anon-func.d/expected.tags b/Units/parser-javascript.r/self-invoking-anon-func.d/expected.tags new file mode 100644 index 0000000000..a5bf2f6fe2 --- /dev/null +++ b/Units/parser-javascript.r/self-invoking-anon-func.d/expected.tags @@ -0,0 +1,10 @@ +anonymousFunction204aa80e0100 input.js /^(function(x) {$/;" f +a input.js /^ var a = function (o) { return o; };$/;" f function:anonymousFunction204aa80e0100 +anonymousFunction204aa80e0200 input.js /^(function(y) {$/;" f +b input.js /^ const b = function (p) { return p; };$/;" f function:anonymousFunction204aa80e0200 +anonymousFunction204aa80e0300 input.js /^!function(z) {$/;" f +c input.js /^ const c = function (r) { return r; };$/;" f function:anonymousFunction204aa80e0300 +anonymousFunction204aa80e0400 input.js /^console.log("a" + (function (A) {$/;" f +anonymousFunction204aa80e0500 input.js /^}((function (B) {$/;" f +anonymousFunction204aa80e0600 input.js /^}(!function(C) {$/;" f +f input.js /^ var f = function (t) {$/;" f function:anonymousFunction204aa80e0600 diff --git a/Units/parser-javascript.r/self-invoking-anon-func.d/input.js b/Units/parser-javascript.r/self-invoking-anon-func.d/input.js new file mode 100644 index 0000000000..d8ebac59af --- /dev/null +++ b/Units/parser-javascript.r/self-invoking-anon-func.d/input.js @@ -0,0 +1,26 @@ +(function(x) { + var a = function (o) { return o; }; + return x + a (1); +})(1); + +(function(y) { + const b = function (p) { return p; }; + return y - b (1); +}(1)); + +!function(z) { + const c = function (r) { return r; }; + return z * c (1); +}(1); + +console.log("a" + (function (A) { + return A; +}((function (B) { + return B; +}(!function(C) { + var f = function (t) { + return !t; + } + return f(C); +}(true) + ))))); diff --git a/parsers/jscript.c b/parsers/jscript.c index d5e6c2aa88..7723659c13 100644 --- a/parsers/jscript.c +++ b/parsers/jscript.c @@ -126,6 +126,7 @@ typedef enum eTokenType { TOKEN_BINARY_OPERATOR, TOKEN_ARROW, TOKEN_DOTS, /* ... */ + TOKEN_BANG, /* ! */ } tokenType; typedef struct sTokenInfo { @@ -1096,6 +1097,9 @@ static void readTokenFullRaw (tokenInfo *const token, bool include_newlines, vSt case '*': token->type = TOKEN_STAR; break; + case '!': + token->type = TOKEN_BANG; + break; case '%': case '?': case '>': @@ -2954,6 +2958,83 @@ static bool parseLine (tokenInfo *const token, bool is_inside_class) break; } } + else if (isType (token, TOKEN_BANG)) + { + /* + * Try to handle "Self-Invoking Anonymous Function Statements" like: + * + * !function(z) { # case A. + * return z * 1; + * }(1); + * + * case A is nothing special; just consuming the token for ! is enough. + */ + readToken (token); + is_terminated = parseLine (token, is_inside_class); + } + else if (isType (token, TOKEN_OPEN_PAREN)) + { + /* + * Try to handle "Self-Invoking Anonymous Function Statements" like: + * + * (function(x) { # case B. + * return x + 1; + * })(1); + * + * (function(y) { # case C. + * return y - 1; + * }(1)); + * + */ + readToken (token); + is_terminated = parseLine (token, is_inside_class); + + /* + * In case B, next ')' should be consumed here. + * In case C, the last ')' after an argument list should be consumed here. + * In case B and C, this function returns true. + */ + if (is_terminated) + { + tokenType last_token_type = token->type; + + readToken (token); + if (isType (token, TOKEN_CLOSE_PAREN)) + { + /* case B. */ + is_terminated = true; + } + else if (last_token_type == TOKEN_CLOSE_CURLY && isType (token, TOKEN_OPEN_PAREN)) + { + /* + * maybe case C. + * Parse the argument list if there is. + */ + is_terminated = parseStatement (token, is_inside_class); + if (is_terminated) + { + readToken (token); + if (!isType (token, TOKEN_CLOSE_PAREN) && !isType (token, TOKEN_EOF)) + { + /* + * In case B, ')' terminates the statement. + * If the token is not ')', the statement is + * not a "Self-Invoking Anonymous Function Statement". + */ + Assert (NextToken == NULL); + NextToken = newToken (); + copyToken (NextToken, token, false); + } + } + } + else if (!isType (token, TOKEN_EOF)) + { + Assert (NextToken == NULL); + NextToken = newToken (); + copyToken (NextToken, token, false); + } + } + } else { /* @@ -3051,32 +3132,36 @@ static const char *kindName(jsKind kind) } static const char *tokenTypeName(enum eTokenType e) -{ /* Generated by misc/enumstr.sh with cmdline "parsers/jscript.c" "eTokenType" "tokenTypeName" */ +{ /* Generated by misc/enumstr.sh with cmdline: + parsers/jscript.c eTokenType tokenTypeName */ switch (e) { - case TOKEN_BINARY_OPERATOR: return "TOKEN_BINARY_OPERATOR"; - case TOKEN_CHARACTER: return "TOKEN_CHARACTER"; - case TOKEN_CLOSE_CURLY: return "TOKEN_CLOSE_CURLY"; - case TOKEN_CLOSE_PAREN: return "TOKEN_CLOSE_PAREN"; - case TOKEN_CLOSE_SQUARE: return "TOKEN_CLOSE_SQUARE"; - case TOKEN_COLON: return "TOKEN_COLON"; - case TOKEN_COMMA: return "TOKEN_COMMA"; - case TOKEN_EOF: return "TOKEN_EOF"; - case TOKEN_EQUAL_SIGN: return "TOKEN_EQUAL_SIGN"; - case TOKEN_IDENTIFIER: return "TOKEN_IDENTIFIER"; - case TOKEN_KEYWORD: return "TOKEN_KEYWORD"; - case TOKEN_OPEN_CURLY: return "TOKEN_OPEN_CURLY"; - case TOKEN_OPEN_PAREN: return "TOKEN_OPEN_PAREN"; - case TOKEN_OPEN_SQUARE: return "TOKEN_OPEN_SQUARE"; - case TOKEN_PERIOD: return "TOKEN_PERIOD"; - case TOKEN_POSTFIX_OPERATOR: return "TOKEN_POSTFIX_OPERATOR"; - case TOKEN_REGEXP: return "TOKEN_REGEXP"; - case TOKEN_SEMICOLON: return "TOKEN_SEMICOLON"; - case TOKEN_STAR: return "TOKEN_STAR"; - case TOKEN_STRING: return "TOKEN_STRING"; - case TOKEN_TEMPLATE_STRING: return "TOKEN_TEMPLATE_STRING"; - case TOKEN_UNDEFINED: return "TOKEN_UNDEFINED"; - default: return "UNKNOWN"; + case TOKEN_UNDEFINED: return "TOKEN_UNDEFINED"; + case TOKEN_EOF: return "TOKEN_EOF"; + case TOKEN_CHARACTER: return "TOKEN_CHARACTER"; + case TOKEN_CLOSE_PAREN: return "TOKEN_CLOSE_PAREN"; + case TOKEN_SEMICOLON: return "TOKEN_SEMICOLON"; + case TOKEN_COLON: return "TOKEN_COLON"; + case TOKEN_COMMA: return "TOKEN_COMMA"; + case TOKEN_KEYWORD: return "TOKEN_KEYWORD"; + case TOKEN_OPEN_PAREN: return "TOKEN_OPEN_PAREN"; + case TOKEN_IDENTIFIER: return "TOKEN_IDENTIFIER"; + case TOKEN_STRING: return "TOKEN_STRING"; + case TOKEN_TEMPLATE_STRING: return "TOKEN_TEMPLATE_STRING"; + case TOKEN_PERIOD: return "TOKEN_PERIOD"; + case TOKEN_OPEN_CURLY: return "TOKEN_OPEN_CURLY"; + case TOKEN_CLOSE_CURLY: return "TOKEN_CLOSE_CURLY"; + case TOKEN_EQUAL_SIGN: return "TOKEN_EQUAL_SIGN"; + case TOKEN_OPEN_SQUARE: return "TOKEN_OPEN_SQUARE"; + case TOKEN_CLOSE_SQUARE: return "TOKEN_CLOSE_SQUARE"; + case TOKEN_REGEXP: return "TOKEN_REGEXP"; + case TOKEN_POSTFIX_OPERATOR: return "TOKEN_POSTFIX_OPERATOR"; + case TOKEN_STAR: return "TOKEN_STAR"; + case TOKEN_ATMARK: return "TOKEN_ATMARK"; + case TOKEN_BINARY_OPERATOR: return "TOKEN_BINARY_OPERATOR"; + case TOKEN_ARROW: return "TOKEN_ARROW"; + case TOKEN_DOTS: return "TOKEN_DOTS"; + default: return "UNKNOWN"; } } #endif