Skip to content

Commit

Permalink
JavaScript: extract variables and constants in destructuring assignment
Browse files Browse the repository at this point in the history
Close universal-ctags#1112.

Signed-off-by: Masatake YAMATO <[email protected]>
  • Loading branch information
masatake committed Aug 26, 2022
1 parent dfffd3d commit ec3b205
Show file tree
Hide file tree
Showing 6 changed files with 275 additions and 2 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
--sort=no
59 changes: 59 additions & 0 deletions Units/parser-javascript.r/js-destructural-binding.d/expected.tags
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
x input.js /^const [x] = [1];$/;" C
y input.js /^const [y, z] = [1, 2, 3, 4, 5];$/;" C
z input.js /^const [y, z] = [1, 2, 3, 4, 5];$/;" C
a input.js /^let [a=5, b=7] = [1];$/;" v
b input.js /^let [a=5, b=7] = [1];$/;" v
c input.js /^let [c, , d] = [1, 2, 3];$/;" v
d input.js /^let [c, , d] = [1, 2, 3];$/;" v
e input.js /^let [e, f = 0, , g] = [1, 2, 3, 4];$/;" v
f input.js /^let [e, f = 0, , g] = [1, 2, 3, 4];$/;" v
g input.js /^let [e, f = 0, , g] = [1, 2, 3, 4];$/;" v
h input.js /^const [h, i, ...[j, k]] = [1, 2, 3, 4];$/;" C
i input.js /^const [h, i, ...[j, k]] = [1, 2, 3, 4];$/;" C
j input.js /^const [h, i, ...[j, k]] = [1, 2, 3, 4];$/;" C
k input.js /^const [h, i, ...[j, k]] = [1, 2, 3, 4];$/;" C
l input.js /^const [l, m, ...[n, o, ...[p, q]]] = [1, 2, 3, 4, 5, 6];$/;" C
m input.js /^const [l, m, ...[n, o, ...[p, q]]] = [1, 2, 3, 4, 5, 6];$/;" C
n input.js /^const [l, m, ...[n, o, ...[p, q]]] = [1, 2, 3, 4, 5, 6];$/;" C
o input.js /^const [l, m, ...[n, o, ...[p, q]]] = [1, 2, 3, 4, 5, 6];$/;" C
p input.js /^const [l, m, ...[n, o, ...[p, q]]] = [1, 2, 3, 4, 5, 6];$/;" C
q input.js /^const [l, m, ...[n, o, ...[p, q]]] = [1, 2, 3, 4, 5, 6];$/;" C
A input.js /^const [A, B, ...{ C, D }] = []$/;" C
B input.js /^const [A, B, ...{ C, D }] = []$/;" C
C input.js /^const [A, B, ...{ C, D }] = []$/;" C
D input.js /^const [A, B, ...{ C, D }] = []$/;" C
E input.js /^ E: 42,$/;" p variable:user
F input.js /^ F: true$/;" p variable:user
user input.js /^const user = {$/;" v
E input.js /^const {E, F} = user;$/;" C
F input.js /^const {E, F} = user;$/;" C
G input.js /^const {E: G, F: H} = user;$/;" C
H input.js /^const {E: G, F: H} = user;$/;" C
I input.js /^const {I = 10, J = 5} = {I: 3};$/;" C
J input.js /^const {I = 10, J = 5} = {I: 3};$/;" C
I input.js /^const {I = 10, J = 5} = {I: 3};$/;" p variable:anonymousObject785a93f40105
anonymousObject785a93f40105 input.js /^const {I = 10, J = 5} = {I: 3};$/;" v
K input.js /^let {a: K = 10, b: L = 5} = {a: 3};$/;" v
L input.js /^let {a: K = 10, b: L = 5} = {a: 3};$/;" v
a input.js /^let {a: K = 10, b: L = 5} = {a: 3};$/;" p variable:anonymousObject785a93f40205
anonymousObject785a93f40205 input.js /^let {a: K = 10, b: L = 5} = {a: 3};$/;" v
M input.js /^let {M, N, ...O} = {M: 10, N: 20, c: 30, d: 40}$/;" v
N input.js /^let {M, N, ...O} = {M: 10, N: 20, c: 30, d: 40}$/;" v
O input.js /^let {M, N, ...O} = {M: 10, N: 20, c: 30, d: 40}$/;" v
M input.js /^let {M, N, ...O} = {M: 10, N: 20, c: 30, d: 40}$/;" p variable:anonymousObject785a93f40305
N input.js /^let {M, N, ...O} = {M: 10, N: 20, c: 30, d: 40}$/;" p variable:anonymousObject785a93f40305
c input.js /^let {M, N, ...O} = {M: 10, N: 20, c: 30, d: 40}$/;" p variable:anonymousObject785a93f40305
d input.js /^let {M, N, ...O} = {M: 10, N: 20, c: 30, d: 40}$/;" p variable:anonymousObject785a93f40305
anonymousObject785a93f40305 input.js /^let {M, N, ...O} = {M: 10, N: 20, c: 30, d: 40}$/;" v
title input.js /^ title: 'Scratchpad',$/;" p variable:metadata
anonymousObject785a93f40405 input.js /^ {$/;" v variable:metadata
locale input.js /^ locale: 'de',$/;" p variable:metadata.anonymousObject785a93f40405
localization_tags input.js /^ localization_tags: [],$/;" p variable:metadata.anonymousObject785a93f40405
last_edit input.js /^ last_edit: '2014-04-14T08:43:37',$/;" p variable:metadata.anonymousObject785a93f40405
url input.js /^ url: '\/de\/docs\/Tools\/Scratchpad',$/;" p variable:metadata.anonymousObject785a93f40405
title input.js /^ title: 'JavaScript-Umgebung'$/;" p variable:metadata.anonymousObject785a93f40405
translations input.js /^ translations: [$/;" p variable:metadata
url input.js /^ url: '\/en-US\/docs\/Tools\/Scratchpad'$/;" p variable:metadata
metadata input.js /^const metadata = {$/;" v
englishTitle input.js /^ title: englishTitle, \/\/ rename$/;" v
localeTitle input.js /^ title: localeTitle, \/\/ rename$/;" v
56 changes: 56 additions & 0 deletions Units/parser-javascript.r/js-destructural-binding.d/input.js
Original file line number Diff line number Diff line change
@@ -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;
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
node
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
prop input.js /^var {prop} = { prop: "value" };$/;" v
prop input.js /^var {prop} = { prop: "value" };$/;" p variable:anonymousObject4ca5b60a0105
anonymousObject4ca5b60a0105 input.js /^var {prop} = { prop: "value" };$/;" v
159 changes: 157 additions & 2 deletions parsers/jscript.c
Original file line number Diff line number Diff line change
Expand Up @@ -2637,7 +2637,8 @@ static bool parseStatementRHS (tokenInfo *const name, tokenInfo *const token, st

if (state->indexForName == CORK_NIL)
{
state->indexForName = makeJsTag (name, state->isConst ? JSTAG_CONSTANT : JSTAG_VARIABLE, NULL, NULL);
if (!vStringIsEmpty (name->string))
state->indexForName = makeJsTag (name, state->isConst ? JSTAG_CONSTANT : JSTAG_VARIABLE, NULL, NULL);
if (isType (token, TOKEN_IDENTIFIER))
canbe_arrowfun = true;
}
Expand Down Expand Up @@ -2706,6 +2707,143 @@ static bool parseStatementRHS (tokenInfo *const name, tokenInfo *const token, st
return true;
}

static bool parseObjectDestructuring (tokenInfo *const token, bool is_const);
static bool parseArrayDestructuring (tokenInfo *const token, bool is_const)
{
int nest_level = 1;
bool in_left_side = true;
bool found = false;

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;
if (parseObjectDestructuring (token, is_const))
found = true;
}
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);
found = true;
}
else if (isType (token, TOKEN_EQUAL_SIGN))
{
in_left_side = false;
/* TODO: SKIP */
}
else
in_left_side = false;
}

return found;
}

static bool parseObjectDestructuring (tokenInfo *const token, bool is_const)
{
tokenInfo *const name = newToken ();
bool found = false;

/*
* 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))
{
if (parseObjectDestructuring (token, is_const))
found = true;
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);
found = true;
}
break;
}
else if (isType (token, TOKEN_OPEN_SQUARE))
{
if (parseArrayDestructuring (token, is_const))
found = true;
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);
found = true;
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);
found = true;
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);
return found;
}

static bool parseStatement (tokenInfo *const token, bool is_inside_class)
{
TRACE_ENTER_TEXT("is_inside_class: %s", is_inside_class? "yes": "no");
Expand Down Expand Up @@ -2773,6 +2911,22 @@ static bool parseStatement (tokenInfo *const token, bool is_inside_class)
state.isGlobal = true;
}
readToken(token);

if (state.isGlobal)
{
bool found = false;
if (isType (token, TOKEN_OPEN_CURLY))
found = parseObjectDestructuring (token, state.isConst);
else if (isType (token, TOKEN_OPEN_SQUARE))
found = parseArrayDestructuring (token, state.isConst);

if (found)
{
/* Adjust the context to the code for non-destructing. */
found_lhs = true;
readToken(token);
}
}
}

nextVar:
Expand Down Expand Up @@ -2860,7 +3014,8 @@ static bool parseStatement (tokenInfo *const token, bool is_inside_class)
* Handles this syntax:
* var g_var2;
*/
state.indexForName = makeJsTag (name, state.isConst ? JSTAG_CONSTANT : JSTAG_VARIABLE, NULL, NULL);
if (!vStringIsEmpty (name->string))
state.indexForName = makeJsTag (name, state.isConst ? JSTAG_CONSTANT : JSTAG_VARIABLE, NULL, NULL);
}
/*
* Statement has ended.
Expand Down

0 comments on commit ec3b205

Please sign in to comment.