diff --git a/Units/parser-javascript.r/js-destructural-binding.d/args.ctags b/Units/parser-javascript.r/js-destructural-binding.d/args.ctags new file mode 100644 index 0000000000..5ee5f79f70 --- /dev/null +++ b/Units/parser-javascript.r/js-destructural-binding.d/args.ctags @@ -0,0 +1 @@ +--sort=no diff --git a/Units/parser-javascript.r/js-destructural-binding.d/expected.tags b/Units/parser-javascript.r/js-destructural-binding.d/expected.tags new file mode 100644 index 0000000000..e69de29bb2 diff --git a/Units/parser-javascript.r/js-destructural-binding.d/input.js b/Units/parser-javascript.r/js-destructural-binding.d/input.js new file mode 100644 index 0000000000..5fc18b740f --- /dev/null +++ b/Units/parser-javascript.r/js-destructural-binding.d/input.js @@ -0,0 +1,56 @@ +// Derrived from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment +const [x] = [1]; +const [y, z] = [1, 2, 3, 4, 5]; +let [a=5, b=7] = [1]; + +let [c, , d] = [1, 2, 3]; +let [e, f = 0, , g] = [1, 2, 3, 4]; + +let [,,] = [1, 2, 3]; +let [,] = [1, 2, 3]; +let [] = [1, 2, 3]; + +const [h, i, ...[j, k]] = [1, 2, 3, 4]; +const [l, m, ...[n, o, ...[p, q]]] = [1, 2, 3, 4, 5, 6]; + +const [A, B, ...{ C, D }] = [] + +const user = { + E: 42, + F: true +}; + +const {E, F} = user; + +const {E: G, F: H} = user; + + +const {I = 10, J = 5} = {I: 3}; + +let {a: K = 10, b: L = 5} = {a: 3}; + +let {M, N, ...O} = {M: 10, N: 20, c: 30, d: 40} + + +const metadata = { + title: 'Scratchpad', + translations: [ + { + locale: 'de', + localization_tags: [], + last_edit: '2014-04-14T08:43:37', + url: '/de/docs/Tools/Scratchpad', + title: 'JavaScript-Umgebung' + } + ], + url: '/en-US/docs/Tools/Scratchpad' +}; + +let { + title: englishTitle, // rename + translations: [ + { + title: localeTitle, // rename + }, + ], +} = metadata; diff --git a/Units/parser-javascript.r/js-destructural-binding.d/validator b/Units/parser-javascript.r/js-destructural-binding.d/validator new file mode 100644 index 0000000000..64f5a0a681 --- /dev/null +++ b/Units/parser-javascript.r/js-destructural-binding.d/validator @@ -0,0 +1 @@ +node diff --git a/parsers/jscript.c b/parsers/jscript.c index 4b55e90929..1ef6d254bb 100644 --- a/parsers/jscript.c +++ b/parsers/jscript.c @@ -2252,6 +2252,121 @@ static bool parseES6Class (tokenInfo *const token, const tokenInfo *targetName) return true; } +static void parseObjectDestructuring (tokenInfo *const token, bool is_const); +static void parseArrayDestructuring (tokenInfo *const token, bool is_const) +{ + int nest_level = 1; + bool in_left_side = true; + + while (nest_level > 0 && ! isType (token, TOKEN_EOF)) + { + readToken (token); + if (isType (token, TOKEN_OPEN_SQUARE)) + { + in_left_side = true; + nest_level++; + } + else if (isType (token, TOKEN_CLOSE_SQUARE)) + { + in_left_side = false; + nest_level--; + } + else if (isType (token, TOKEN_OPEN_CURLY)) + { + in_left_side = false; + parseObjectDestructuring (token, is_const); + } + else if (isType (token, TOKEN_COMMA) + || isType (token, TOKEN_DOTS)) + in_left_side = true; + else if (in_left_side && isType (token, TOKEN_IDENTIFIER)) + { + in_left_side = false; + makeJsTag (token, is_const ? JSTAG_CONSTANT : JSTAG_VARIABLE, NULL, NULL); + } + else if (isType (token, TOKEN_EQUAL_SIGN)) + { + in_left_side = false; + /* TODO: SKIP */ + } + else + in_left_side = false; + } +} + +static void parseObjectDestructuring (tokenInfo *const token, bool is_const) +{ + tokenInfo *const name = newToken (); + + /* + * let { k0: v0, k1: v1 = 0, v3 }; + * | | || | | | + * ^...|..|^...|..|....^.....: start + * ....^..|....^..|..........: colon + * .......^.......^..........: emitted (made a tag for an id after colon) + */ + enum objDestructuringState { + OBJ_DESTRUCTURING_START, + OBJ_DESTRUCTURING_COLON, + OBJ_DESTRUCTURING_EMITTED, + } state = OBJ_DESTRUCTURING_START; + + while (! isType (token, TOKEN_EOF)) + { + readToken (token); + if (isType (token, TOKEN_OPEN_CURLY)) + { + parseObjectDestructuring (token, is_const); + if (state == OBJ_DESTRUCTURING_COLON) + state = OBJ_DESTRUCTURING_EMITTED; + } + else if (isType (token, TOKEN_CLOSE_CURLY)) + { + if (!vStringIsEmpty(name->string)) + makeJsTag (name, is_const ? JSTAG_CONSTANT : JSTAG_VARIABLE, NULL, NULL); + break; + } + else if (isType (token, TOKEN_OPEN_SQUARE)) + { + parseArrayDestructuring (token, is_const); + if (state == OBJ_DESTRUCTURING_COLON) + state = OBJ_DESTRUCTURING_EMITTED; + } + else if (isType (token, TOKEN_IDENTIFIER)) + { + if (state == OBJ_DESTRUCTURING_COLON) + { + makeJsTag (token, is_const ? JSTAG_CONSTANT : JSTAG_VARIABLE, NULL, NULL); + state = OBJ_DESTRUCTURING_EMITTED; + } + else if (state == OBJ_DESTRUCTURING_START + && vStringIsEmpty(name->string)) + copyToken(name, token, true); + } + else if (isType (token, TOKEN_COMMA)) + { + if (!vStringIsEmpty(name->string)) + { + makeJsTag (name, is_const ? JSTAG_CONSTANT : JSTAG_VARIABLE, NULL, NULL); + vStringClear (name->string); + } + state = OBJ_DESTRUCTURING_START; + } + else if (isType (token, TOKEN_COLON)) + { + vStringClear (name->string); + state = OBJ_DESTRUCTURING_COLON; + } + else + { + if (state == OBJ_DESTRUCTURING_COLON) + state = OBJ_DESTRUCTURING_EMITTED; + } + } + + deleteToken (name); +} + static bool parseStatement (tokenInfo *const token, bool is_inside_class) { TRACE_ENTER_TEXT("is_inside_class: %s", is_inside_class? "yes": "no"); @@ -2322,6 +2437,14 @@ static bool parseStatement (tokenInfo *const token, bool is_inside_class) is_global = true; } readToken(token); + + if (is_global) + { + if (isType (token, TOKEN_OPEN_CURLY)) + parseObjectDestructuring (token, is_const); + else if (isType (token, TOKEN_OPEN_SQUARE)) + parseArrayDestructuring (token, is_const); + } } nextVar: @@ -2530,7 +2653,8 @@ static bool parseStatement (tokenInfo *const token, bool is_inside_class) * Handles this syntax: * var g_var2; */ - indexForName = makeJsTag (name, is_const ? JSTAG_CONSTANT : JSTAG_VARIABLE, NULL, NULL); + if (! vStringIsEmpty (name->string)) + indexForName = makeJsTag (name, is_const ? JSTAG_CONSTANT : JSTAG_VARIABLE, NULL, NULL); } /* * Statement has ended. @@ -2749,7 +2873,8 @@ static bool parseStatement (tokenInfo *const token, bool is_inside_class) } } else if (! isType (token, TOKEN_KEYWORD) && - token->nestLevel == 0 && is_global ) + token->nestLevel == 0 && is_global && + (! vStringIsEmpty (name->string))) { /* * Only create variables for global scope