From 0e9f467a68aeebda5b94b865102c1058bc7adff0 Mon Sep 17 00:00:00 2001 From: Snehil Shah Date: Mon, 20 May 2024 17:52:37 +0000 Subject: [PATCH 01/23] feat: add syntax highlighting in the REPL Signed-off-by: Snehil Shah --- .../@stdlib/repl/lib/ansi_colors.js | 58 +++++++ lib/node_modules/@stdlib/repl/lib/main.js | 5 + .../@stdlib/repl/lib/syntax_highlighter.js | 148 ++++++++++++++++++ lib/node_modules/@stdlib/repl/lib/themes.js | 44 ++++++ .../@stdlib/repl/lib/tokenizer.js | 128 +++++++++++++++ 5 files changed, 383 insertions(+) create mode 100644 lib/node_modules/@stdlib/repl/lib/ansi_colors.js create mode 100644 lib/node_modules/@stdlib/repl/lib/syntax_highlighter.js create mode 100644 lib/node_modules/@stdlib/repl/lib/themes.js create mode 100644 lib/node_modules/@stdlib/repl/lib/tokenizer.js diff --git a/lib/node_modules/@stdlib/repl/lib/ansi_colors.js b/lib/node_modules/@stdlib/repl/lib/ansi_colors.js new file mode 100644 index 000000000000..1df33d1bea42 --- /dev/null +++ b/lib/node_modules/@stdlib/repl/lib/ansi_colors.js @@ -0,0 +1,58 @@ +/** +* @license Apache-2.0 +* +* Copyright (c) 2024 The Stdlib Authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +'use strict'; + +// MAIN // + +/** +* Table mapping generic color names to their ANSI color codes. +* +* @private +* @name ANSI +* @type {Object} +*/ +var ANSI = { + // Original colors: + 'black': '\u001b[30m', + 'red': '\u001b[31m', + 'green': '\u001b[32m', + 'yellow': '\u001b[33m', + 'blue': '\u001b[34m', + 'magenta': '\u001b[35m', + 'cyan': '\u001b[36m', + 'white': '\u001b[37m', + + // Bright colors: + 'brightBlack': '\u001b[90m', + 'brightRed': '\u001b[91m', + 'brightGreen': '\u001b[92m', + 'brightYellow': '\u001b[93m', + 'brightBlue': '\u001b[94m', + 'brightMagenta': '\u001b[95m', + 'brightCyan': '\u001b[96m', + 'brightWhite': '\u001b[97m', + + // Reset colors: + 'reset': '\u001b[0m' +}; + + +// EXPORTS // + +module.exports = ANSI; diff --git a/lib/node_modules/@stdlib/repl/lib/main.js b/lib/node_modules/@stdlib/repl/lib/main.js index b5afa1583a11..56e92fd9e1bf 100644 --- a/lib/node_modules/@stdlib/repl/lib/main.js +++ b/lib/node_modules/@stdlib/repl/lib/main.js @@ -63,6 +63,7 @@ var processLine = require( './process_line.js' ); var completerFactory = require( './completer.js' ); var PreviewCompleter = require( './completer_preview.js' ); var AutoCloser = require( './auto_close_pairs.js' ); +var SyntaxHighlighter = require( './syntax_highlighter.js' ); var ALIAS_OVERRIDES = require( './alias_overrides.js' ); var SETTINGS = require( './settings.js' ); var SETTINGS_VALIDATORS = require( './settings_validators.js' ); @@ -270,6 +271,9 @@ function REPL( options ) { // Initialize a preview completer: setNonEnumerableReadOnly( this, '_previewCompleter', new PreviewCompleter( this._rli, this._completer, this._ostream, this._settings.completionPreviews ) ); + // Initialize a syntax-highlighter: + setNonEnumerableReadOnly( this, '_syntaxHighlighter', new SyntaxHighlighter( this, this._ostream ) ); + // Cache a reference to the private readline interface `ttyWrite` to allow calling the method when wanting default behavior: setNonEnumerableReadOnly( this, '_ttyWrite', this._rli._ttyWrite ); @@ -343,6 +347,7 @@ function REPL( options ) { if ( autoClosed ) { self._previewCompleter.clear(); } + self._syntaxHighlighter.onKeypress( data ); self._previewCompleter.onKeypress( data, key ); } diff --git a/lib/node_modules/@stdlib/repl/lib/syntax_highlighter.js b/lib/node_modules/@stdlib/repl/lib/syntax_highlighter.js new file mode 100644 index 000000000000..dad5a1926756 --- /dev/null +++ b/lib/node_modules/@stdlib/repl/lib/syntax_highlighter.js @@ -0,0 +1,148 @@ +/** +* @license Apache-2.0 +* +* Copyright (c) 2024 The Stdlib Authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +/* eslint-disable no-underscore-dangle, no-restricted-syntax, no-invalid-this */ + +'use strict'; + +// MODULES // + +var readline = require( 'readline' ); +var logger = require( 'debug' ); +var setNonEnumerableReadOnly = require( '@stdlib/utils/define-nonenumerable-read-only-property' ); +var tokenizer = require( './tokenizer.js' ); +var THEMES = require( './themes.js' ); +var ANSI = require( './ansi_colors.js' ); + + +// VARIABLES // + +var debug = logger( 'repl:syntax-highlighter' ); + + +// MAIN // + +/** +* Constructor for creating a syntax-highlighter. +* +* @private +* @constructor +* @param {repl} repl - REPL instance +* @param {WritableStream} ostream - writable stream +* @returns {SyntaxHighlighter} syntax-highlighter instance +*/ +function SyntaxHighlighter( repl, ostream ) { + if ( !( this instanceof SyntaxHighlighter ) ) { + return new SyntaxHighlighter( repl, ostream ); + } + debug( 'Creating a new syntax-highlighter' ); + + // Cache a reference to the provided REPL instance: + this._repl = repl; + + // Cache a reference to readline interface: + this._rli = repl._rli; + + // Cache a reference to the output writable stream: + this._ostream = ostream; + + // Cache a reference to the available themes: + this._themes = THEMES; + + // Initialize a buffer containing the current theme: + this._theme = 'default'; + + return this; +} + +/** +* Highlights the input line. +* +* @private +* @name _highlightLine +* @memberof SyntaxHighlighter.prototype +* @type {Function} +* @param {str} line - input line +* @param {Array} tokens - array of tokens in the line +* @returns {str} highlighted line +*/ +setNonEnumerableReadOnly( SyntaxHighlighter.prototype, '_highlightLine', function highlightLine( line, tokens ) { + var highlightedLine; + var resetCode = ANSI[ 'reset' ]; + var colorCode; + var colors = this._themes[ this._theme ]; + var offset; + var token; + + highlightedLine = line.split( '' ); // split into array for easy insertions + offset = 0; // track number of ANSI codes inserted + for ( token of tokens ) { + colorCode = ANSI[ colors[ token.type ] ]; + + // Highlight token if it's color exists in the theme... + if ( colorCode ) { + highlightedLine.splice( token.start + offset, 0, colorCode ); // insert colorCode + offset += 1; + highlightedLine.splice( token.end + offset, 0, resetCode ); // reset color + offset += 1; + } + } + return highlightedLine.join( '' ); +}); + +/** +* Callback for handling a "keypress" event. +* +* @name onKeypress +* @memberof SyntaxHighlighter.prototype +* @type {Function} +* @param {string} data - input data +* @param {(Object|void)} key - key object +* @returns {void} +*/ +setNonEnumerableReadOnly( SyntaxHighlighter.prototype, 'onKeypress', function onKeypress( data ) { + var highlightedLine; + var tokens; + var line; + + line = this._rli.line; + if ( !data || !line ) { + debug( 'Empty line or no change detected. Skipping highlighting...' ); + return; + } + + debug( 'Tokenizing line: ' + line ); + tokens = tokenizer( line ); + if ( !tokens ) { + debug( 'No tokens found. Skipping highlighting...' ); + return; + } + debug( tokens.length + ' tokens found. Highlighting...' ); + highlightedLine = this._highlightLine( line, tokens ); + + debug( 'Replacing current line with the highlighted line...' ); + readline.moveCursor( this._ostream, -1 * this._rli.cursor, 0 ); + readline.clearLine( this._ostream, 1 ); + this._ostream.write( highlightedLine ); + readline.moveCursor( this._ostream, this._rli.cursor - line.length, 0 ); +}); + + +// EXPORTS // + +module.exports = SyntaxHighlighter; diff --git a/lib/node_modules/@stdlib/repl/lib/themes.js b/lib/node_modules/@stdlib/repl/lib/themes.js new file mode 100644 index 000000000000..8e801c1fb124 --- /dev/null +++ b/lib/node_modules/@stdlib/repl/lib/themes.js @@ -0,0 +1,44 @@ +/** +* @license Apache-2.0 +* +* Copyright (c) 2024 The Stdlib Authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +'use strict'; + +// MAIN // + +/** +* Object containing themes where each theme is an table mapping tokens to their respective colors. +* +* @private +* @name THEMES +* @type {Object} +*/ +var THEMES = { + 'default': { + 'comment': 'brightBlack', + 'loop': 'magenta', + 'string': 'yellow', + 'number': 'green', + 'keyword': 'blue', + 'command': 'cyan' + } +}; + + +// EXPORTS // + +module.exports = THEMES; diff --git a/lib/node_modules/@stdlib/repl/lib/tokenizer.js b/lib/node_modules/@stdlib/repl/lib/tokenizer.js new file mode 100644 index 000000000000..70c9510297aa --- /dev/null +++ b/lib/node_modules/@stdlib/repl/lib/tokenizer.js @@ -0,0 +1,128 @@ +/** +* @license Apache-2.0 +* +* Copyright (c) 2024 The Stdlib Authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +'use strict'; + +// MODULES // + +var parse = require( 'acorn-loose' ).parse; +var commands = require( './commands.js' ); + + +// VARIABLES // + +var COMMANDS = commands(); + + +// MAIN // + +/** +* Tokenizes the input line based on ECMAScript 2020 specification. +* +* @private +* @name tokenizer +* @type {Function} +* @param {string} line - input line +* @returns {Array} array of tokens +*/ +function tokenizer( line ) { + var tokens = []; + + parse( line, { + 'ecmaVersion': 11, + 'onToken': onToken, + 'onComment': onComment + }); + + /** + * Callback for handling a `Token` event when parsing. + * + * @private + * @param {Object} token - token object + */ + function onToken( token ) { + var command; + + // Ignore non-existent tokens: + if ( token.start === token.end ) { + return; + } + if ( token.type.isLoop ) { + // `for`, `while`, `do`: + token.type = 'loop'; + tokens.push( token ); + return; + } + if ( token.type.keyword ) { + // `function`, `import`, `var`, `const` etc.: + token.type = 'keyword'; + tokens.push( token ); + return; + } + if ( token.type.label === 'string' || token.type.label === 'regexp' || token.type.label === 'template' ) { + // Strings, regex expressions and template string literals: + token.type = 'string'; + tokens.push( token ); + return; + } + if ( token.type.label === 'num' ) { + // Numeric literals: + token.type = 'number'; + tokens.push( token ); + return; + } + if ( token.type.label === 'name' ) { + // REPL commands: + for ( command of COMMANDS ) { + if ( token.value === command[ 0 ] ) { + token.type = 'command'; + tokens.push( token ); + return; + } + } + } + } + + /** + * Callback for handling a `Comment` event when parsing. + * + * @private + * @param {Object} block - true if the comment is a block comment, false if it is a line comment + * @param {string} text - comment value + * @param {number} start - start index + * @param {number} end - end index + */ + function onComment( block, text, start, end ) { + if ( block ) { + // TODO: add support for highlighting multi-line comments. + return; + } + tokens.push({ + 'type': 'comment', + 'start': start, + 'end': end + }); + } + + return tokens; +} + + +// EXPORTS // + +module.exports = tokenizer; From ceaa8c0c1f90b15cbe92dee8d58857f56fe12d84 Mon Sep 17 00:00:00 2001 From: Snehil Shah Date: Tue, 21 May 2024 09:57:24 +0000 Subject: [PATCH 02/23] fix: cache line to avoid highlighting when no change detected Signed-off-by: Snehil Shah --- lib/node_modules/@stdlib/repl/lib/main.js | 3 ++- .../@stdlib/repl/lib/syntax_highlighter.js | 22 ++++++++++++------- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/lib/node_modules/@stdlib/repl/lib/main.js b/lib/node_modules/@stdlib/repl/lib/main.js index 56e92fd9e1bf..134bfedf62a8 100644 --- a/lib/node_modules/@stdlib/repl/lib/main.js +++ b/lib/node_modules/@stdlib/repl/lib/main.js @@ -338,6 +338,8 @@ function REPL( options ) { */ function onKeypress( data, key ) { var autoClosed; + + self._syntaxHighlighter.onKeypress(); if ( key && key.name === 'tab' ) { return; } @@ -347,7 +349,6 @@ function REPL( options ) { if ( autoClosed ) { self._previewCompleter.clear(); } - self._syntaxHighlighter.onKeypress( data ); self._previewCompleter.onKeypress( data, key ); } diff --git a/lib/node_modules/@stdlib/repl/lib/syntax_highlighter.js b/lib/node_modules/@stdlib/repl/lib/syntax_highlighter.js index dad5a1926756..187769c90ea8 100644 --- a/lib/node_modules/@stdlib/repl/lib/syntax_highlighter.js +++ b/lib/node_modules/@stdlib/repl/lib/syntax_highlighter.js @@ -67,6 +67,9 @@ function SyntaxHighlighter( repl, ostream ) { // Initialize a buffer containing the current theme: this._theme = 'default'; + // Initialize a buffer containing the current line to validate line changes: + this._line = ''; + return this; } @@ -115,31 +118,34 @@ setNonEnumerableReadOnly( SyntaxHighlighter.prototype, '_highlightLine', functio * @param {(Object|void)} key - key object * @returns {void} */ -setNonEnumerableReadOnly( SyntaxHighlighter.prototype, 'onKeypress', function onKeypress( data ) { +setNonEnumerableReadOnly( SyntaxHighlighter.prototype, 'onKeypress', function onKeypress() { var highlightedLine; var tokens; - var line; - line = this._rli.line; - if ( !data || !line ) { + if ( !this._rli.line || this._line === this._rli.line ) { debug( 'Empty line or no change detected. Skipping highlighting...' ); return; } + this._line = this._rli.line; - debug( 'Tokenizing line: ' + line ); - tokens = tokenizer( line ); + // Tokenize: + debug( 'Tokenizing line: ' + this._line ); + tokens = tokenizer( this._line ); if ( !tokens ) { debug( 'No tokens found. Skipping highlighting...' ); return; } + + // Highlight: debug( tokens.length + ' tokens found. Highlighting...' ); - highlightedLine = this._highlightLine( line, tokens ); + highlightedLine = this._highlightLine( this._line, tokens ); + // Replace: debug( 'Replacing current line with the highlighted line...' ); readline.moveCursor( this._ostream, -1 * this._rli.cursor, 0 ); readline.clearLine( this._ostream, 1 ); this._ostream.write( highlightedLine ); - readline.moveCursor( this._ostream, this._rli.cursor - line.length, 0 ); + readline.moveCursor( this._ostream, this._rli.cursor - this._line.length, 0 ); // eslint-disable-line max-len }); From c422e7131e12d791430dc735232de663cf3781ce Mon Sep 17 00:00:00 2001 From: Snehil Shah Date: Tue, 21 May 2024 10:42:45 +0000 Subject: [PATCH 03/23] feat: explicitly define keywords like `let` Signed-off-by: Snehil Shah --- lib/node_modules/@stdlib/repl/lib/tokenizer.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/node_modules/@stdlib/repl/lib/tokenizer.js b/lib/node_modules/@stdlib/repl/lib/tokenizer.js index 70c9510297aa..03b5613ab26b 100644 --- a/lib/node_modules/@stdlib/repl/lib/tokenizer.js +++ b/lib/node_modules/@stdlib/repl/lib/tokenizer.js @@ -32,7 +32,7 @@ var COMMANDS = commands(); // MAIN // /** -* Tokenizes the input line based on ECMAScript 2020 specification. +* Tokenizes the input line based on ECMAScript specification. * * @private * @name tokenizer @@ -44,7 +44,7 @@ function tokenizer( line ) { var tokens = []; parse( line, { - 'ecmaVersion': 11, + 'ecmaVersion': 'latest', 'onToken': onToken, 'onComment': onComment }); @@ -62,14 +62,14 @@ function tokenizer( line ) { if ( token.start === token.end ) { return; } - if ( token.type.isLoop ) { - // `for`, `while`, `do`: + if ( token.type.isLoop || ( token.type.label === 'name' && [ 'async', 'await' ].includes( token.value ) ) ) { + // Asynchronous and looping keywords: `for`, `while`, `do`, `async`, `await`: token.type = 'loop'; tokens.push( token ); return; } - if ( token.type.keyword ) { - // `function`, `import`, `var`, `const` etc.: + if ( token.type.keyword || ( token.type.label === 'name' && token.value === 'let' ) ) { + // Keywords: `function`, `import`, `var`, `const`, `let` etc.: token.type = 'keyword'; tokens.push( token ); return; From 295633fcc268d0057dd94756e0f7fcb264ef7717 Mon Sep 17 00:00:00 2001 From: Snehil Shah Date: Sat, 25 May 2024 05:24:31 +0000 Subject: [PATCH 04/23] style: stick to conventions Signed-off-by: Snehil Shah --- lib/node_modules/@stdlib/repl/lib/syntax_highlighter.js | 8 +++++--- lib/node_modules/@stdlib/repl/lib/tokenizer.js | 7 +++++-- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/lib/node_modules/@stdlib/repl/lib/syntax_highlighter.js b/lib/node_modules/@stdlib/repl/lib/syntax_highlighter.js index 187769c90ea8..67c871b1445b 100644 --- a/lib/node_modules/@stdlib/repl/lib/syntax_highlighter.js +++ b/lib/node_modules/@stdlib/repl/lib/syntax_highlighter.js @@ -91,10 +91,12 @@ setNonEnumerableReadOnly( SyntaxHighlighter.prototype, '_highlightLine', functio var colors = this._themes[ this._theme ]; var offset; var token; + var i; highlightedLine = line.split( '' ); // split into array for easy insertions offset = 0; // track number of ANSI codes inserted - for ( token of tokens ) { + for ( i = 0; i < tokens.length; i++ ) { + token = tokens[ i ]; colorCode = ANSI[ colors[ token.type ] ]; // Highlight token if it's color exists in the theme... @@ -129,7 +131,7 @@ setNonEnumerableReadOnly( SyntaxHighlighter.prototype, 'onKeypress', function on this._line = this._rli.line; // Tokenize: - debug( 'Tokenizing line: ' + this._line ); + debug( 'Tokenizing line: %s', this._line ); tokens = tokenizer( this._line ); if ( !tokens ) { debug( 'No tokens found. Skipping highlighting...' ); @@ -137,7 +139,7 @@ setNonEnumerableReadOnly( SyntaxHighlighter.prototype, 'onKeypress', function on } // Highlight: - debug( tokens.length + ' tokens found. Highlighting...' ); + debug( '%s tokens found. Highlighting...', tokens.length ); highlightedLine = this._highlightLine( this._line, tokens ); // Replace: diff --git a/lib/node_modules/@stdlib/repl/lib/tokenizer.js b/lib/node_modules/@stdlib/repl/lib/tokenizer.js index 03b5613ab26b..86a4c6c2a995 100644 --- a/lib/node_modules/@stdlib/repl/lib/tokenizer.js +++ b/lib/node_modules/@stdlib/repl/lib/tokenizer.js @@ -21,6 +21,7 @@ // MODULES // var parse = require( 'acorn-loose' ).parse; +var contains = require('@stdlib/array/base/assert/contains'); var commands = require( './commands.js' ); @@ -57,12 +58,13 @@ function tokenizer( line ) { */ function onToken( token ) { var command; + var i; // Ignore non-existent tokens: if ( token.start === token.end ) { return; } - if ( token.type.isLoop || ( token.type.label === 'name' && [ 'async', 'await' ].includes( token.value ) ) ) { + if ( token.type.isLoop || ( token.type.label === 'name' && contains( [ 'async', 'await' ], token.value ) ) ) { // Asynchronous and looping keywords: `for`, `while`, `do`, `async`, `await`: token.type = 'loop'; tokens.push( token ); @@ -88,7 +90,8 @@ function tokenizer( line ) { } if ( token.type.label === 'name' ) { // REPL commands: - for ( command of COMMANDS ) { + for ( i = 0; i < COMMANDS.length; i++ ) { + command = COMMANDS[ i ]; if ( token.value === command[ 0 ] ) { token.type = 'command'; tokens.push( token ); From 5af1fa72e1921bb975fda81a663205f8931fd0be Mon Sep 17 00:00:00 2001 From: Snehil Shah Date: Sat, 25 May 2024 05:39:10 +0000 Subject: [PATCH 05/23] fix: suggestions Signed-off-by: Snehil Shah --- lib/node_modules/@stdlib/repl/lib/main.js | 2 +- .../@stdlib/repl/lib/syntax_highlighter.js | 23 ++++++------------- 2 files changed, 8 insertions(+), 17 deletions(-) diff --git a/lib/node_modules/@stdlib/repl/lib/main.js b/lib/node_modules/@stdlib/repl/lib/main.js index 134bfedf62a8..36655341d8ad 100644 --- a/lib/node_modules/@stdlib/repl/lib/main.js +++ b/lib/node_modules/@stdlib/repl/lib/main.js @@ -272,7 +272,7 @@ function REPL( options ) { setNonEnumerableReadOnly( this, '_previewCompleter', new PreviewCompleter( this._rli, this._completer, this._ostream, this._settings.completionPreviews ) ); // Initialize a syntax-highlighter: - setNonEnumerableReadOnly( this, '_syntaxHighlighter', new SyntaxHighlighter( this, this._ostream ) ); + setNonEnumerableReadOnly( this, '_syntaxHighlighter', new SyntaxHighlighter( this._rli, this._ostream ) ); // Cache a reference to the private readline interface `ttyWrite` to allow calling the method when wanting default behavior: setNonEnumerableReadOnly( this, '_ttyWrite', this._rli._ttyWrite ); diff --git a/lib/node_modules/@stdlib/repl/lib/syntax_highlighter.js b/lib/node_modules/@stdlib/repl/lib/syntax_highlighter.js index 67c871b1445b..8a4168e831be 100644 --- a/lib/node_modules/@stdlib/repl/lib/syntax_highlighter.js +++ b/lib/node_modules/@stdlib/repl/lib/syntax_highlighter.js @@ -16,7 +16,7 @@ * limitations under the License. */ -/* eslint-disable no-underscore-dangle, no-restricted-syntax, no-invalid-this */ +/* eslint-disable no-restricted-syntax, no-invalid-this */ 'use strict'; @@ -42,31 +42,22 @@ var debug = logger( 'repl:syntax-highlighter' ); * * @private * @constructor -* @param {repl} repl - REPL instance +* @param {repl} rli - readline instance * @param {WritableStream} ostream - writable stream * @returns {SyntaxHighlighter} syntax-highlighter instance */ -function SyntaxHighlighter( repl, ostream ) { +function SyntaxHighlighter( rli, ostream ) { if ( !( this instanceof SyntaxHighlighter ) ) { - return new SyntaxHighlighter( repl, ostream ); + return new SyntaxHighlighter( rli, ostream ); } debug( 'Creating a new syntax-highlighter' ); - // Cache a reference to the provided REPL instance: - this._repl = repl; - - // Cache a reference to readline interface: - this._rli = repl._rli; + // Cache a reference to the provided readline interface: + this._rli = rli; // Cache a reference to the output writable stream: this._ostream = ostream; - // Cache a reference to the available themes: - this._themes = THEMES; - - // Initialize a buffer containing the current theme: - this._theme = 'default'; - // Initialize a buffer containing the current line to validate line changes: this._line = ''; @@ -88,7 +79,7 @@ setNonEnumerableReadOnly( SyntaxHighlighter.prototype, '_highlightLine', functio var highlightedLine; var resetCode = ANSI[ 'reset' ]; var colorCode; - var colors = this._themes[ this._theme ]; + var colors = THEMES[ 'default' ]; var offset; var token; var i; From c4d174851f5dc107351a85645ba190753c279be7 Mon Sep 17 00:00:00 2001 From: Snehil Shah Date: Sat, 25 May 2024 16:02:36 +0000 Subject: [PATCH 06/23] feat: add more token types Signed-off-by: Snehil Shah --- lib/node_modules/@stdlib/repl/lib/main.js | 6 +- .../@stdlib/repl/lib/syntax_highlighter.js | 51 ++++-- lib/node_modules/@stdlib/repl/lib/themes.js | 26 ++- .../@stdlib/repl/lib/tokenizer.js | 168 +++++++++++++++--- 4 files changed, 207 insertions(+), 44 deletions(-) diff --git a/lib/node_modules/@stdlib/repl/lib/main.js b/lib/node_modules/@stdlib/repl/lib/main.js index 36655341d8ad..d812cf445cef 100644 --- a/lib/node_modules/@stdlib/repl/lib/main.js +++ b/lib/node_modules/@stdlib/repl/lib/main.js @@ -272,7 +272,7 @@ function REPL( options ) { setNonEnumerableReadOnly( this, '_previewCompleter', new PreviewCompleter( this._rli, this._completer, this._ostream, this._settings.completionPreviews ) ); // Initialize a syntax-highlighter: - setNonEnumerableReadOnly( this, '_syntaxHighlighter', new SyntaxHighlighter( this._rli, this._ostream ) ); + setNonEnumerableReadOnly( this, '_syntaxHighlighter', new SyntaxHighlighter( this, this._ostream ) ); // Cache a reference to the private readline interface `ttyWrite` to allow calling the method when wanting default behavior: setNonEnumerableReadOnly( this, '_ttyWrite', this._rli._ttyWrite ); @@ -339,7 +339,6 @@ function REPL( options ) { function onKeypress( data, key ) { var autoClosed; - self._syntaxHighlighter.onKeypress(); if ( key && key.name === 'tab' ) { return; } @@ -349,6 +348,9 @@ function REPL( options ) { if ( autoClosed ) { self._previewCompleter.clear(); } + if ( self._isTTY ) { + self._syntaxHighlighter.onKeypress(); + } self._previewCompleter.onKeypress( data, key ); } diff --git a/lib/node_modules/@stdlib/repl/lib/syntax_highlighter.js b/lib/node_modules/@stdlib/repl/lib/syntax_highlighter.js index 8a4168e831be..ad3dc40ba4b3 100644 --- a/lib/node_modules/@stdlib/repl/lib/syntax_highlighter.js +++ b/lib/node_modules/@stdlib/repl/lib/syntax_highlighter.js @@ -16,7 +16,7 @@ * limitations under the License. */ -/* eslint-disable no-restricted-syntax, no-invalid-this */ +/* eslint-disable no-restricted-syntax, no-invalid-this, no-underscore-dangle */ 'use strict'; @@ -35,6 +35,21 @@ var ANSI = require( './ansi_colors.js' ); var debug = logger( 'repl:syntax-highlighter' ); +// FUNCTIONS // + +/** +* Compare function for sorting tokens in ascending order of their indices. +* +* @private +* @param {Object} a - first token +* @param {Object} b - second token +* @returns {boolean} boolean indicating if the first token is greater +*/ +function tokenComparator( a, b ) { + return a.start - b.start; +} + + // MAIN // /** @@ -42,18 +57,21 @@ var debug = logger( 'repl:syntax-highlighter' ); * * @private * @constructor -* @param {repl} rli - readline instance +* @param {REPL} repl - REPL instance * @param {WritableStream} ostream - writable stream * @returns {SyntaxHighlighter} syntax-highlighter instance */ -function SyntaxHighlighter( rli, ostream ) { +function SyntaxHighlighter( repl, ostream ) { if ( !( this instanceof SyntaxHighlighter ) ) { - return new SyntaxHighlighter( rli, ostream ); + return new SyntaxHighlighter( repl, ostream ); } debug( 'Creating a new syntax-highlighter' ); + // Cache a reference to the provided REPL instance: + this._repl = repl; + // Cache a reference to the provided readline interface: - this._rli = rli; + this._rli = repl._rli; // Cache a reference to the output writable stream: this._ostream = ostream; @@ -76,29 +94,32 @@ function SyntaxHighlighter( rli, ostream ) { * @returns {str} highlighted line */ setNonEnumerableReadOnly( SyntaxHighlighter.prototype, '_highlightLine', function highlightLine( line, tokens ) { - var highlightedLine; + var highlightedLine = ''; var resetCode = ANSI[ 'reset' ]; var colorCode; var colors = THEMES[ 'default' ]; - var offset; + var offset = 0; var token; var i; - highlightedLine = line.split( '' ); // split into array for easy insertions - offset = 0; // track number of ANSI codes inserted + // Sort and traverse the tokens... + tokens.sort( tokenComparator ); for ( i = 0; i < tokens.length; i++ ) { token = tokens[ i ]; colorCode = ANSI[ colors[ token.type ] ]; // Highlight token if it's color exists in the theme... if ( colorCode ) { - highlightedLine.splice( token.start + offset, 0, colorCode ); // insert colorCode - offset += 1; - highlightedLine.splice( token.end + offset, 0, resetCode ); // reset color - offset += 1; + highlightedLine += line.slice( offset, token.start ); // add text before token + highlightedLine += colorCode; // insert colorCode + highlightedLine += line.slice( token.start, token.end ); // add token + highlightedLine += resetCode; // reset color + offset = token.end; } } - return highlightedLine.join( '' ); + highlightedLine += line.slice( offset ); // add remaining text + + return highlightedLine; }); /** @@ -123,7 +144,7 @@ setNonEnumerableReadOnly( SyntaxHighlighter.prototype, 'onKeypress', function on // Tokenize: debug( 'Tokenizing line: %s', this._line ); - tokens = tokenizer( this._line ); + tokens = tokenizer( this._line, this._repl._context ); if ( !tokens ) { debug( 'No tokens found. Skipping highlighting...' ); return; diff --git a/lib/node_modules/@stdlib/repl/lib/themes.js b/lib/node_modules/@stdlib/repl/lib/themes.js index 8e801c1fb124..6b6a8ac55dc9 100644 --- a/lib/node_modules/@stdlib/repl/lib/themes.js +++ b/lib/node_modules/@stdlib/repl/lib/themes.js @@ -29,12 +29,28 @@ */ var THEMES = { 'default': { - 'comment': 'brightBlack', - 'loop': 'magenta', - 'string': 'yellow', - 'number': 'green', + // Keywords: + 'control': 'magenta', 'keyword': 'blue', - 'command': 'cyan' + 'specialIdentifier': 'cyan', + + // Literals: + 'string': 'brightYellow', + 'number': 'brightGreen', + 'literal': 'brightblue', + 'regexp': 'red', + + // Identifiers: + 'command': 'cyan', + 'functionIdentifier': 'yellow', + 'objectIdentifier': 'brightCyan', + 'numberIdentifier': null, + 'stringIdentifier': null, + + // Others: + 'comment': 'brightBlack', + 'punctuation': null, + 'operator': null } }; diff --git a/lib/node_modules/@stdlib/repl/lib/tokenizer.js b/lib/node_modules/@stdlib/repl/lib/tokenizer.js index 86a4c6c2a995..bbbf69125d7d 100644 --- a/lib/node_modules/@stdlib/repl/lib/tokenizer.js +++ b/lib/node_modules/@stdlib/repl/lib/tokenizer.js @@ -21,6 +21,8 @@ // MODULES // var parse = require( 'acorn-loose' ).parse; +var walk = require( 'acorn-walk' ); +var linkedList = require( '@stdlib/utils/linked-list' ); var contains = require('@stdlib/array/base/assert/contains'); var commands = require( './commands.js' ); @@ -28,6 +30,7 @@ var commands = require( './commands.js' ); // VARIABLES // var COMMANDS = commands(); +var RE_PUNCTUATION = /(\{|\}|\[|\]|\(|\)|,|;|:|\.|\?|\?\.|=>|\.\.\.|`|\${)/; // MAIN // @@ -39,12 +42,16 @@ var COMMANDS = commands(); * @name tokenizer * @type {Function} * @param {string} line - input line +* @param {Object} context - REPL context * @returns {Array} array of tokens */ -function tokenizer( line ) { +function tokenizer( line, context ) { + var VISITORS; var tokens = []; + var ast; - parse( line, { + // Parse the given line into meaningful tokens... + ast = parse( line, { 'ecmaVersion': 'latest', 'onToken': onToken, 'onComment': onComment @@ -57,47 +64,62 @@ function tokenizer( line ) { * @param {Object} token - token object */ function onToken( token ) { - var command; - var i; - // Ignore non-existent tokens: if ( token.start === token.end ) { return; } - if ( token.type.isLoop || ( token.type.label === 'name' && contains( [ 'async', 'await' ], token.value ) ) ) { - // Asynchronous and looping keywords: `for`, `while`, `do`, `async`, `await`: - token.type = 'loop'; + if ( token.type.isLoop || contains( [ 'if', 'else', 'switch', 'case', 'catch', 'finally', 'try' ], token.type.keyword ) ) { + // Control flow keywords - `for`, `while`, `do`, `if`, `else` etc: + token.type = 'control'; + tokens.push( token ); + return; + } + if ( contains( [ 'this', 'super' ], token.type.keyword ) ) { + // Special identifiers - `this`, `super`: + token.type = 'specialIdentifier'; tokens.push( token ); return; } - if ( token.type.keyword || ( token.type.label === 'name' && token.value === 'let' ) ) { - // Keywords: `function`, `import`, `var`, `const`, `let` etc.: + if ( contains( [ 'null', 'true', 'false' ], token.type.keyword ) || ( token.type.label === 'name' && token.value === 'undefined' ) ) { + // Built-in literals - `true`, `false`, `null`, `undefined`: + token.type = 'literal'; + tokens.push( token ); + } + if ( token.type.keyword || ( token.type.label === 'name' && contains( [ 'async', 'await', 'let' ], token.value ) ) ) { + // Keywords - `function`, `import`, `var`, `const`, `let` etc.: token.type = 'keyword'; tokens.push( token ); return; } - if ( token.type.label === 'string' || token.type.label === 'regexp' || token.type.label === 'template' ) { - // Strings, regex expressions and template string literals: + if ( token.type.label === 'string' || token.type.label === 'template' ) { + // Strings and template string literals: token.type = 'string'; tokens.push( token ); return; } + if ( token.type.label === 'regexp' ) { + // Regex expressions: + token.type = 'regexp'; + tokens.push( token ); + return; + } if ( token.type.label === 'num' ) { // Numeric literals: token.type = 'number'; tokens.push( token ); return; } - if ( token.type.label === 'name' ) { - // REPL commands: - for ( i = 0; i < COMMANDS.length; i++ ) { - command = COMMANDS[ i ]; - if ( token.value === command[ 0 ] ) { - token.type = 'command'; - tokens.push( token ); - return; - } - } + if ( token.type.binop || token.type.prefix || token.type.postfix || token.type.isAssign ) { // eslint-disable-line max-len + // Operator symbols - `+`, `=`, `++` etc: + token.type = 'operator'; + tokens.push( token ); + return; + } + if ( RE_PUNCTUATION.test( token.type.label ) ) { + // Punctuation symbols - `,`, `(`, `;` etc: + token.value = token.type.label; + token.type = 'punctuation'; + tokens.push( token ); } } @@ -122,6 +144,108 @@ function tokenizer( line ) { }); } + // Resolve global identifiers from given line... + VISITORS = { + 'Identifier': Identifier, + 'MemberExpression': MemberExpression + }; + walk.simple( ast, VISITORS ); + + /** + * Handles `Identifier` nodes. + * + * @private + * @param {Object} node - `Identifier` node. + */ + function Identifier( node ) { + var identifier; + var command; + var i; + + // If identifier is a REPL command, push it as a token... + for ( i = 0; i < COMMANDS.length; i++ ) { + command = COMMANDS[ i ]; + if ( node.name === command[ 0 ] ) { + tokens.push( { + 'value': node.name, + 'type': 'command', + 'start': node.start, + 'end': node.end + }); + return; + } + } + // If identifier is found in global context, push it as a token... + identifier = context[ node.name ]; + if ( identifier ) { + tokens.push( { + 'value': node.name, + 'type': typeof identifier + 'Identifier', + 'start': node.start, + 'end': node.end + }); + } + } + + /** + * Handles `MemberExpression` nodes. + * + * @private + * @param {Object} node - `MemberExpression` node. + */ + function MemberExpression( node ) { + var properties = linkedList(); + var property; + var obj = context; + + /** + * Recursively traverses the `MemberExpression` node and populates the `properties` array. + * + * @private + * @param {Object} node - node to be traversed + */ + function traverse( node ) { + if ( node.object.type === 'Identifier' ) { + // Reached a resolvable MemberExpression (a.b in a.b.c.d), save property(b) & object(a) and exit: + properties.unshift( node.property ); + properties.unshift( node.object ); + } else if ( node.object.type === 'MemberExpression' ) { + // Found node with another MemberExpression as object, save property and traverse it: + properties.unshift( node.property ); + traverse( node.object ); + } + } + traverse( node ); + + if ( !properties.first() ) { + // Not an object: + return; + } + // Enter first property's namespace as the first property is already getting tokenized by the `Identifier` handler: + properties = properties.iterator(); + obj = obj[ properties.next().value.name ]; + if ( !obj ) { + // Object not defined in context: + return; + } + property = properties.next(); + while ( !property.done ) { + // Fetch identifier value from context: + obj = obj[ property.value.name ]; + if ( !obj ) { + break; + } + // Push token if identifier exists in context: + tokens.push({ + 'value': property.value.name, + 'type': typeof obj + 'Identifier', + 'start': property.value.start, + 'end': property.value.end + }); + property = properties.next(); + } + } + return tokens; } From 1ebc27f0e93a6341fcfa0a8a6af97b2673ea9df0 Mon Sep 17 00:00:00 2001 From: Snehil Shah Date: Sun, 26 May 2024 07:33:41 +0000 Subject: [PATCH 07/23] fix: add missing `return` statement Signed-off-by: Snehil Shah --- lib/node_modules/@stdlib/repl/lib/tokenizer.js | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/node_modules/@stdlib/repl/lib/tokenizer.js b/lib/node_modules/@stdlib/repl/lib/tokenizer.js index bbbf69125d7d..a239785393f8 100644 --- a/lib/node_modules/@stdlib/repl/lib/tokenizer.js +++ b/lib/node_modules/@stdlib/repl/lib/tokenizer.js @@ -84,6 +84,7 @@ function tokenizer( line, context ) { // Built-in literals - `true`, `false`, `null`, `undefined`: token.type = 'literal'; tokens.push( token ); + return; } if ( token.type.keyword || ( token.type.label === 'name' && contains( [ 'async', 'await', 'let' ], token.value ) ) ) { // Keywords - `function`, `import`, `var`, `const`, `let` etc.: From 3c19a6b671effae24259ad1956ca785b542cde56 Mon Sep 17 00:00:00 2001 From: Snehil Shah Date: Sun, 26 May 2024 10:56:54 +0000 Subject: [PATCH 08/23] docs: change jsdoc Signed-off-by: Snehil Shah --- lib/node_modules/@stdlib/repl/lib/tokenizer.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/node_modules/@stdlib/repl/lib/tokenizer.js b/lib/node_modules/@stdlib/repl/lib/tokenizer.js index a239785393f8..305121f6d223 100644 --- a/lib/node_modules/@stdlib/repl/lib/tokenizer.js +++ b/lib/node_modules/@stdlib/repl/lib/tokenizer.js @@ -58,7 +58,7 @@ function tokenizer( line, context ) { }); /** - * Callback for handling a `Token` event when parsing. + * Callback invoked upon encountering a `Token` when parsing. * * @private * @param {Object} token - token object @@ -125,7 +125,7 @@ function tokenizer( line, context ) { } /** - * Callback for handling a `Comment` event when parsing. + * Callback invoked upon encountering a `Comment` when parsing. * * @private * @param {Object} block - true if the comment is a block comment, false if it is a line comment @@ -153,10 +153,10 @@ function tokenizer( line, context ) { walk.simple( ast, VISITORS ); /** - * Handles `Identifier` nodes. + * Callback invoked upon encountering a `Identifier` AST node. * * @private - * @param {Object} node - `Identifier` node. + * @param {Object} node - AST node */ function Identifier( node ) { var identifier; @@ -189,10 +189,10 @@ function tokenizer( line, context ) { } /** - * Handles `MemberExpression` nodes. + * Callback invoked upon encountering a `MemberExpression` AST node. * * @private - * @param {Object} node - `MemberExpression` node. + * @param {Object} node - AST node */ function MemberExpression( node ) { var properties = linkedList(); @@ -200,7 +200,7 @@ function tokenizer( line, context ) { var obj = context; /** - * Recursively traverses the `MemberExpression` node and populates the `properties` array. + * Recursively traverses the `MemberExpression` node to populate the `properties` array. * * @private * @param {Object} node - node to be traversed From 1e4ea8c8bd3c827728d4205627771b392aa0bab3 Mon Sep 17 00:00:00 2001 From: Snehil Shah Date: Sun, 26 May 2024 14:53:47 +0000 Subject: [PATCH 09/23] fix: incorrect color name Signed-off-by: Snehil Shah --- lib/node_modules/@stdlib/repl/lib/themes.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/node_modules/@stdlib/repl/lib/themes.js b/lib/node_modules/@stdlib/repl/lib/themes.js index 6b6a8ac55dc9..4c8c3462ea85 100644 --- a/lib/node_modules/@stdlib/repl/lib/themes.js +++ b/lib/node_modules/@stdlib/repl/lib/themes.js @@ -37,7 +37,7 @@ var THEMES = { // Literals: 'string': 'brightYellow', 'number': 'brightGreen', - 'literal': 'brightblue', + 'literal': 'brightBlue', 'regexp': 'red', // Identifiers: From 33eb6a0e53a57d5b3fc1e56d0c750d41e901091f Mon Sep 17 00:00:00 2001 From: Snehil Shah Date: Sun, 26 May 2024 17:19:52 +0000 Subject: [PATCH 10/23] feat: resolve local identifiers & declarations Signed-off-by: Snehil Shah --- .../@stdlib/repl/lib/resolve_local_scopes.js | 354 +++++++++--------- lib/node_modules/@stdlib/repl/lib/themes.js | 10 +- .../@stdlib/repl/lib/tokenizer.js | 98 +++-- 3 files changed, 261 insertions(+), 201 deletions(-) diff --git a/lib/node_modules/@stdlib/repl/lib/resolve_local_scopes.js b/lib/node_modules/@stdlib/repl/lib/resolve_local_scopes.js index 459adde06438..c55af79aa847 100644 --- a/lib/node_modules/@stdlib/repl/lib/resolve_local_scopes.js +++ b/lib/node_modules/@stdlib/repl/lib/resolve_local_scopes.js @@ -27,219 +27,229 @@ var isScope = require( './is_scope.js' ); var isBlockScope = require( './is_block_scope.js' ); -// VARIABLES // - -var VISITORS = { - 'VariableDeclaration': VariableDeclaration, - 'FunctionDeclaration': FunctionDeclaration, - 'Function': declareFunction, - 'ClassDeclaration': ClassDeclaration, - 'TryStatement': TryStatement, - 'ImportDefaultSpecifier': ModuleImportSpecifier, - 'ImportSpecifier': ModuleImportSpecifier, - 'ImportNamespaceSpecifier': ModuleImportSpecifier -}; - - -// FUNCTIONS // +// MAIN // /** -* Resolves identifiers arising from a function declaration, such as function parameters and the function name. +* Resolves local scopes within an abstract syntax tree (AST). +* +* ## Notes +* +* - This function modifies the provided AST by adding a `locals` property to select AST nodes. * * @private -* @param {Node} node - function declaration AST node +* @param {AST} ast - abstract syntax tree (AST) +* @param {Array} out - output array with all declared `Identifier` nodes +* @throws {TypeError} must provide a program AST node +* @returns {AST} input AST */ -function declareFunction( node ) { +function resolveScopes( ast, out ) { + var declarations; + var VISITORS; var i; - node.locals = node.locals || []; - for ( i = 0; i < node.params.length; i++ ) { - declarationPattern( node.params[ i ], node ); + if ( ast.type !== 'Program' ) { + throw new TypeError( 'invalid argument. Must provide a program AST node.' ); } - if ( node.id ) { - appendUnique( node.locals, node.id.name ); + ast.locals = []; + declarations = []; + VISITORS = { + 'VariableDeclaration': VariableDeclaration, + 'FunctionDeclaration': FunctionDeclaration, + 'Function': declareFunction, + 'ClassDeclaration': ClassDeclaration, + 'TryStatement': TryStatement, + 'ImportDefaultSpecifier': ModuleImportSpecifier, + 'ImportSpecifier': ModuleImportSpecifier, + 'ImportNamespaceSpecifier': ModuleImportSpecifier + }; + walk( ast, VISITORS ); + if ( typeof out !== 'undefined' ) { + for ( i = 0; i < declarations.length; i++ ) { + out.push( declarations[ i ] ); + } } -} + return ast; -/** -* Resolves identifiers based on the variable declaration pattern. -* -* @private -* @param {Node} node - AST node -* @param {Node} parent - parent AST node -* @throws {Error} unrecognized pattern type -*/ -function declarationPattern( node, parent ) { - var i; + /** + * Resolves identifiers arising from a function declaration, such as function parameters and the function name. + * + * @private + * @param {Node} node - function declaration AST node + */ + function declareFunction( node ) { + var i; - switch ( node.type ) { // eslint-disable-line padded-blocks + node.locals = node.locals || []; + for ( i = 0; i < node.params.length; i++ ) { + declarationPattern( node.params[ i ], node ); + } + if ( node.id ) { + appendUnique( node.locals, node.id.name ); + appendUnique( declarations, node.id ); + } + } - // Actual identifier name: - case 'Identifier': - appendUnique( parent.locals, node.name ); - break; + /** + * Resolves identifiers based on the variable declaration pattern. + * + * @private + * @param {Node} node - AST node + * @param {Node} parent - parent AST node + * @throws {Error} unrecognized pattern type + */ + function declarationPattern( node, parent ) { + var i; - // `var { a, b } = { 'a': 10, 'b': 20 }` || `var { ...o } = {}` - case 'ObjectPattern': - for ( i = 0; i < node.properties.length; i++ ) { - declarationPattern( node.properties[ i ].value || node.properties[ i ].argument, parent ); // eslint-disable-line max-len - } - break; + switch ( node.type ) { // eslint-disable-line padded-blocks + + // Actual identifier name: + case 'Identifier': + appendUnique( parent.locals, node.name ); + appendUnique( declarations, node ); + break; - // `var [ x, y ] = [ 10, 20 ]` - case 'ArrayPattern': - for ( i = 0; i < node.elements.length; i++ ) { - if ( node.elements[ i ] ) { - declarationPattern( node.elements[ i ], parent ); + // `var { a, b } = { 'a': 10, 'b': 20 }` || `var { ...o } = {}` + case 'ObjectPattern': + for ( i = 0; i < node.properties.length; i++ ) { + declarationPattern( node.properties[ i ].value || node.properties[ i ].argument, parent ); // eslint-disable-line max-len } - } - break; + break; - // `var [ x, y, ...z ] = [ 10, 20, 30, 40, 50 ]` || `var [ ...z ] = []` - case 'RestElement': - declarationPattern( node.argument, parent ); - break; + // `var [ x, y ] = [ 10, 20 ]` + case 'ArrayPattern': + for ( i = 0; i < node.elements.length; i++ ) { + if ( node.elements[ i ] ) { + declarationPattern( node.elements[ i ], parent ); + } + } + break; - // `var [ x=5, y=7] = [ 1 ]` - case 'AssignmentPattern': - declarationPattern( node.left, parent ); - break; + // `var [ x, y, ...z ] = [ 10, 20, 30, 40, 50 ]` || `var [ ...z ] = []` + case 'RestElement': + declarationPattern( node.argument, parent ); + break; + + // `var [ x=5, y=7] = [ 1 ]` + case 'AssignmentPattern': + declarationPattern( node.left, parent ); + break; - default: - throw new Error( format( 'internal error. Unrecognized pattern type: `%s`.', node.type ) ); + default: + throw new Error( format( 'internal error. Unrecognized pattern type: `%s`.', node.type ) ); + } } -} -/** -* Callback invoked upon encountering a `VariableDeclaration` AST node. -* -* @private -* @param {Node} node - AST node -* @param {Array} parents - array of parent AST nodes -*/ -function VariableDeclaration( node, parents ) { - var parent; - var i; + /** + * Callback invoked upon encountering a `VariableDeclaration` AST node. + * + * @private + * @param {Node} node - AST node + * @param {Array} parents - array of parent AST nodes + */ + function VariableDeclaration( node, parents ) { + var parent; + var i; - // Case: `var x` - if ( node.kind === 'var' ) { - for ( i = parents.length-1; i >= 0; i-- ) { + // Case: `var x` + if ( node.kind === 'var' ) { + for ( i = parents.length-1; i >= 0; i-- ) { + if ( isScope( parents[ i ] ) ) { + parent = parents[ i ]; + break; + } + } + } + // Case: `let x` || `const x` + else { + for ( i = parents.length-1; i >= 0; i-- ) { + if ( isBlockScope( parents[ i ] ) ) { + parent = parents[ i ]; + break; + } + } + } + parent.locals = parent.locals || []; + for ( i = 0; i < node.declarations.length; i++ ) { + declarationPattern( node.declarations[ i ].id, parent ); + } + } + + /** + * Callback invoked upon encountering a `FunctionDeclaration` AST node. + * + * @private + * @param {Node} node - AST node + * @param {Array} parents - array of parent AST nodes + */ + function FunctionDeclaration( node, parents ) { + var parent; + var i; + for ( i = parents.length-2; i >= 0; i-- ) { if ( isScope( parents[ i ] ) ) { parent = parents[ i ]; break; } } + parent.locals = parent.locals || []; + if ( node.id ) { + appendUnique( parent.locals, node.id.name ); + appendUnique( declarations, node.id ); + } + declareFunction( node ); } - // Case: `let x` || `const x` - else { - for ( i = parents.length-1; i >= 0; i-- ) { + + /** + * Callback invoked upon encountering a `ClassDeclaration` AST node. + * + * @private + * @param {Node} node - AST node + * @param {Array} parents - array of parent AST nodes + */ + function ClassDeclaration( node, parents ) { + var parent; + var i; + for ( i = parents.length-2; i >= 0; i-- ) { if ( isBlockScope( parents[ i ] ) ) { parent = parents[ i ]; break; } } - } - parent.locals = parent.locals || []; - for ( i = 0; i < node.declarations.length; i++ ) { - declarationPattern( node.declarations[ i ].id, parent ); - } -} - -/** -* Callback invoked upon encountering a `FunctionDeclaration` AST node. -* -* @private -* @param {Node} node - AST node -* @param {Array} parents - array of parent AST nodes -*/ -function FunctionDeclaration( node, parents ) { - var parent; - var i; - for ( i = parents.length-2; i >= 0; i-- ) { - if ( isScope( parents[ i ] ) ) { - parent = parents[ i ]; - break; + parent.locals = parent.locals || []; + if ( node.id ) { + appendUnique( parent.locals, node.id.name ); + appendUnique( declarations, node.id ); } } - parent.locals = parent.locals || []; - if ( node.id ) { - appendUnique( parent.locals, node.id.name ); - } - declareFunction( node ); -} -/** -* Callback invoked upon encountering a `ClassDeclaration` AST node. -* -* @private -* @param {Node} node - AST node -* @param {Array} parents - array of parent AST nodes -*/ -function ClassDeclaration( node, parents ) { - var parent; - var i; - for ( i = parents.length-2; i >= 0; i-- ) { - if ( isBlockScope( parents[ i ] ) ) { - parent = parents[ i ]; - break; + /** + * Callback invoked upon encountering a `TryStatement` AST node. + * + * @private + * @param {Node} node - AST node + * @param {Array} parents - array of parent AST nodes + */ + function TryStatement( node ) { + if ( node.handler ) { + node.handler.locals = node.handler.locals || []; + appendUnique( node.handler.locals, node.handler.param.name ); + appendUnique( declarations, node.handler.param ); } } - parent.locals = parent.locals || []; - if ( node.id ) { - appendUnique( parent.locals, node.id.name ); - } -} -/** -* Callback invoked upon encountering a `TryStatement` AST node. -* -* @private -* @param {Node} node - AST node -* @param {Array} parents - array of parent AST nodes -*/ -function TryStatement( node ) { - if ( node.handler ) { - node.handler.locals = node.handler.locals || []; - appendUnique( node.handler.locals, node.handler.param.name ); + /** + * Callback invoked upon encountering a module `import` specifier AST node. + * + * @private + * @param {Node} node - AST node + * @param {Array} parents - array of parent AST nodes + */ + function ModuleImportSpecifier( node, parents ) { + parents[ 0 ].locals = parents[ 0 ].locals || []; + appendUnique( parents[ 0 ].locals, node.local.name ); + appendUnique( declarations, node.local ); } } -/** -* Callback invoked upon encountering a module `import` specifier AST node. -* -* @private -* @param {Node} node - AST node -* @param {Array} parents - array of parent AST nodes -*/ -function ModuleImportSpecifier( node, parents ) { - parents[ 0 ].locals = parents[ 0 ].locals || []; - appendUnique( parents[ 0 ].locals, node.local.name ); -} - - -// MAIN // - -/** -* Resolves local scopes within an abstract syntax tree (AST). -* -* ## Notes -* -* - This function modifies the provided AST by adding a `locals` property to select AST nodes. -* -* @private -* @param {AST} ast - abstract syntax tree (AST) -* @throws {TypeError} must provide a program AST node -* @returns {AST} input AST -*/ -function resolveScopes( ast ) { - if ( ast.type !== 'Program' ) { - throw new TypeError( 'invalid argument. Must provide a program AST node.' ); - } - ast.locals = []; - walk( ast, VISITORS ); - return ast; -} - // EXPORTS // diff --git a/lib/node_modules/@stdlib/repl/lib/themes.js b/lib/node_modules/@stdlib/repl/lib/themes.js index 4c8c3462ea85..5840be5cb198 100644 --- a/lib/node_modules/@stdlib/repl/lib/themes.js +++ b/lib/node_modules/@stdlib/repl/lib/themes.js @@ -41,11 +41,11 @@ var THEMES = { 'regexp': 'red', // Identifiers: - 'command': 'cyan', - 'functionIdentifier': 'yellow', - 'objectIdentifier': 'brightCyan', - 'numberIdentifier': null, - 'stringIdentifier': null, + 'command': 'brightMagenta', + 'function': 'yellow', + 'object': 'brightCyan', + 'variable': null, + 'name': null, // Others: 'comment': 'brightBlack', diff --git a/lib/node_modules/@stdlib/repl/lib/tokenizer.js b/lib/node_modules/@stdlib/repl/lib/tokenizer.js index 305121f6d223..20e4031fd635 100644 --- a/lib/node_modules/@stdlib/repl/lib/tokenizer.js +++ b/lib/node_modules/@stdlib/repl/lib/tokenizer.js @@ -24,6 +24,8 @@ var parse = require( 'acorn-loose' ).parse; var walk = require( 'acorn-walk' ); var linkedList = require( '@stdlib/utils/linked-list' ); var contains = require('@stdlib/array/base/assert/contains'); +var resolveLocalScopes = require( './resolve_local_scopes.js' ); +var resolveLocalScope = require( './resolve_local_scope.js' ); var commands = require( './commands.js' ); @@ -46,17 +48,45 @@ var RE_PUNCTUATION = /(\{|\}|\[|\]|\(|\)|,|;|:|\.|\?|\?\.|=>|\.\.\.|`|\${)/; * @returns {Array} array of tokens */ function tokenizer( line, context ) { + var declarations; var VISITORS; var tokens = []; var ast; + var i; - // Parse the given line into meaningful tokens... + // Parse the given line into tokens & comments... ast = parse( line, { 'ecmaVersion': 'latest', 'onToken': onToken, 'onComment': onComment }); + // Resolve variable declarations from the given line as tokens... + declarations = []; + ast = resolveLocalScopes( ast, declarations ); + if ( declarations ) { + for ( i = 0; i < declarations.length; i++ ) { + if ( declarations[ i ].start === declarations[ i ].end ) { + continue; + } + tokens.push({ + 'type': 'name', + 'value': declarations[ i ].name, + 'start': declarations[ i ].start, + 'end': declarations[ i ].end + }); + } + } + + // Resolve identifiers from the given line as tokens... + VISITORS = { + 'Identifier': Identifier, + 'MemberExpression': MemberExpression + }; + walk.simple( ast, VISITORS ); + + return tokens; + /** * Callback invoked upon encountering a `Token` when parsing. * @@ -145,13 +175,6 @@ function tokenizer( line, context ) { }); } - // Resolve global identifiers from given line... - VISITORS = { - 'Identifier': Identifier, - 'MemberExpression': MemberExpression - }; - walk.simple( ast, VISITORS ); - /** * Callback invoked upon encountering a `Identifier` AST node. * @@ -163,6 +186,16 @@ function tokenizer( line, context ) { var command; var i; + // If identifier is defined in the local scope, assume and treat it like a `variable` and push it as a token... + if ( contains( resolveLocalScope( ast, node ), node.name ) ) { + tokens.push({ + 'value': node.name, + 'type': 'variable', + 'start': node.start, + 'end': node.end + }); + return; + } // If identifier is a REPL command, push it as a token... for ( i = 0; i < COMMANDS.length; i++ ) { command = COMMANDS[ i ]; @@ -176,15 +209,24 @@ function tokenizer( line, context ) { return; } } - // If identifier is found in global context, push it as a token... + // If identifier is in global context, push it as a token... identifier = context[ node.name ]; if ( identifier ) { - tokens.push( { - 'value': node.name, - 'type': typeof identifier + 'Identifier', - 'start': node.start, - 'end': node.end - }); + if ( contains( [ 'boolean', 'string', 'number' ], typeof identifier ) ) { + tokens.push( { + 'value': node.name, + 'type': 'variable', + 'start': node.start, + 'end': node.end + }); + } else { + tokens.push( { + 'value': node.name, + 'type': typeof identifier, + 'start': node.start, + 'end': node.end + }); + } } } @@ -197,7 +239,7 @@ function tokenizer( line, context ) { function MemberExpression( node ) { var properties = linkedList(); var property; - var obj = context; + var obj; /** * Recursively traverses the `MemberExpression` node to populate the `properties` array. @@ -223,6 +265,7 @@ function tokenizer( line, context ) { return; } // Enter first property's namespace as the first property is already getting tokenized by the `Identifier` handler: + obj = context; properties = properties.iterator(); obj = obj[ properties.next().value.name ]; if ( !obj ) { @@ -237,17 +280,24 @@ function tokenizer( line, context ) { break; } // Push token if identifier exists in context: - tokens.push({ - 'value': property.value.name, - 'type': typeof obj + 'Identifier', - 'start': property.value.start, - 'end': property.value.end - }); + if ( contains( [ 'boolean', 'string', 'number' ], typeof obj ) ) { + tokens.push({ + 'value': property.value.name, + 'type': 'variable', + 'start': property.value.start, + 'end': property.value.end + }); + } else { + tokens.push({ + 'value': property.value.name, + 'type': typeof obj, + 'start': property.value.start, + 'end': property.value.end + }); + } property = properties.next(); } } - - return tokens; } From f197dcccc64932bfbfc5088dbdae47d8cf6b31f2 Mon Sep 17 00:00:00 2001 From: Snehil Shah Date: Sun, 26 May 2024 18:27:56 +0000 Subject: [PATCH 11/23] docs: update jsdoc and variable names Signed-off-by: Snehil Shah --- .../@stdlib/repl/lib/resolve_local_scopes.js | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/lib/node_modules/@stdlib/repl/lib/resolve_local_scopes.js b/lib/node_modules/@stdlib/repl/lib/resolve_local_scopes.js index c55af79aa847..244125bae142 100644 --- a/lib/node_modules/@stdlib/repl/lib/resolve_local_scopes.js +++ b/lib/node_modules/@stdlib/repl/lib/resolve_local_scopes.js @@ -38,12 +38,12 @@ var isBlockScope = require( './is_block_scope.js' ); * * @private * @param {AST} ast - abstract syntax tree (AST) -* @param {Array} out - output array with all declared `Identifier` nodes +* @param {Array} declarations - array with all declared `Identifier` nodes appended * @throws {TypeError} must provide a program AST node * @returns {AST} input AST */ -function resolveScopes( ast, out ) { - var declarations; +function resolveScopes( ast, declarations ) { + var identifierNodes; var VISITORS; var i; @@ -51,7 +51,7 @@ function resolveScopes( ast, out ) { throw new TypeError( 'invalid argument. Must provide a program AST node.' ); } ast.locals = []; - declarations = []; + identifierNodes = []; VISITORS = { 'VariableDeclaration': VariableDeclaration, 'FunctionDeclaration': FunctionDeclaration, @@ -64,8 +64,8 @@ function resolveScopes( ast, out ) { }; walk( ast, VISITORS ); if ( typeof out !== 'undefined' ) { - for ( i = 0; i < declarations.length; i++ ) { - out.push( declarations[ i ] ); + for ( i = 0; i < identifierNodes.length; i++ ) { + declarations.push( identifierNodes[ i ] ); } } return ast; @@ -85,7 +85,7 @@ function resolveScopes( ast, out ) { } if ( node.id ) { appendUnique( node.locals, node.id.name ); - appendUnique( declarations, node.id ); + appendUnique( identifierNodes, node.id ); } } @@ -105,7 +105,7 @@ function resolveScopes( ast, out ) { // Actual identifier name: case 'Identifier': appendUnique( parent.locals, node.name ); - appendUnique( declarations, node ); + appendUnique( identifierNodes, node ); break; // `var { a, b } = { 'a': 10, 'b': 20 }` || `var { ...o } = {}` @@ -193,7 +193,7 @@ function resolveScopes( ast, out ) { parent.locals = parent.locals || []; if ( node.id ) { appendUnique( parent.locals, node.id.name ); - appendUnique( declarations, node.id ); + appendUnique( identifierNodes, node.id ); } declareFunction( node ); } @@ -217,7 +217,7 @@ function resolveScopes( ast, out ) { parent.locals = parent.locals || []; if ( node.id ) { appendUnique( parent.locals, node.id.name ); - appendUnique( declarations, node.id ); + appendUnique( identifierNodes, node.id ); } } @@ -232,7 +232,7 @@ function resolveScopes( ast, out ) { if ( node.handler ) { node.handler.locals = node.handler.locals || []; appendUnique( node.handler.locals, node.handler.param.name ); - appendUnique( declarations, node.handler.param ); + appendUnique( identifierNodes, node.handler.param ); } } @@ -246,7 +246,7 @@ function resolveScopes( ast, out ) { function ModuleImportSpecifier( node, parents ) { parents[ 0 ].locals = parents[ 0 ].locals || []; appendUnique( parents[ 0 ].locals, node.local.name ); - appendUnique( declarations, node.local ); + appendUnique( identifierNodes, node.local ); } } From f9ad1723bb4f9bcbf1a836791250dbc7d5070507 Mon Sep 17 00:00:00 2001 From: Snehil Shah Date: Sun, 26 May 2024 18:34:24 +0000 Subject: [PATCH 12/23] feat: update control keywords Signed-off-by: Snehil Shah --- lib/node_modules/@stdlib/repl/lib/tokenizer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/node_modules/@stdlib/repl/lib/tokenizer.js b/lib/node_modules/@stdlib/repl/lib/tokenizer.js index 20e4031fd635..beedc6d5c45a 100644 --- a/lib/node_modules/@stdlib/repl/lib/tokenizer.js +++ b/lib/node_modules/@stdlib/repl/lib/tokenizer.js @@ -98,7 +98,7 @@ function tokenizer( line, context ) { if ( token.start === token.end ) { return; } - if ( token.type.isLoop || contains( [ 'if', 'else', 'switch', 'case', 'catch', 'finally', 'try' ], token.type.keyword ) ) { + if ( token.type.isLoop || contains( [ 'if', 'else', 'switch', 'case', 'catch', 'finally', 'try', 'return', 'break', 'continue' ], token.type.keyword ) ) { // Control flow keywords - `for`, `while`, `do`, `if`, `else` etc: token.type = 'control'; tokens.push( token ); From bfe74087aae325b03ecbe2d0d903d2489f27f512 Mon Sep 17 00:00:00 2001 From: Snehil Shah Date: Mon, 27 May 2024 08:30:37 +0000 Subject: [PATCH 13/23] fix: handle edge cases Signed-off-by: Snehil Shah --- .../@stdlib/repl/lib/tokenizer.js | 26 ++++++++++++++++--- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/lib/node_modules/@stdlib/repl/lib/tokenizer.js b/lib/node_modules/@stdlib/repl/lib/tokenizer.js index beedc6d5c45a..8658f872681d 100644 --- a/lib/node_modules/@stdlib/repl/lib/tokenizer.js +++ b/lib/node_modules/@stdlib/repl/lib/tokenizer.js @@ -186,6 +186,10 @@ function tokenizer( line, context ) { var command; var i; + // Ignore non-existent nodes: + if ( node.start === node.end ) { + return; + } // If identifier is defined in the local scope, assume and treat it like a `variable` and push it as a token... if ( contains( resolveLocalScope( ast, node ), node.name ) ) { tokens.push({ @@ -239,8 +243,14 @@ function tokenizer( line, context ) { function MemberExpression( node ) { var properties = linkedList(); var property; + var locals; var obj; + // Ignore non-existent nodes: + if ( node.start === node.end ) { + return; + } + /** * Recursively traverses the `MemberExpression` node to populate the `properties` array. * @@ -260,11 +270,18 @@ function tokenizer( line, context ) { } traverse( node ); - if ( !properties.first() ) { + // Check if the object identifier is valid... + property = properties.first(); + if ( !property ) { // Not an object: return; } - // Enter first property's namespace as the first property is already getting tokenized by the `Identifier` handler: + // If object identifier exists in the local scope, don't resolve from global scope... + locals = resolveLocalScope( ast, property.value ); + if ( contains( locals, property.value.name ) ) { + return; + } + // Enter object's namespace: obj = context; properties = properties.iterator(); obj = obj[ properties.next().value.name ]; @@ -272,14 +289,15 @@ function tokenizer( line, context ) { // Object not defined in context: return; } + // Fetch properties from context: property = properties.next(); while ( !property.done ) { - // Fetch identifier value from context: obj = obj[ property.value.name ]; if ( !obj ) { + // Property not found in context: break; } - // Push token if identifier exists in context: + // Push token if property exists in context: if ( contains( [ 'boolean', 'string', 'number' ], typeof obj ) ) { tokens.push({ 'value': property.value.name, From a72346a3d7dac58bc1feac19424142a6fb962aa9 Mon Sep 17 00:00:00 2001 From: Snehil Shah Date: Mon, 27 May 2024 09:57:34 +0000 Subject: [PATCH 14/23] fix: use `contains.factory` Signed-off-by: Snehil Shah --- lib/node_modules/@stdlib/repl/lib/tokenizer.js | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/lib/node_modules/@stdlib/repl/lib/tokenizer.js b/lib/node_modules/@stdlib/repl/lib/tokenizer.js index 8658f872681d..d8f2b5e65606 100644 --- a/lib/node_modules/@stdlib/repl/lib/tokenizer.js +++ b/lib/node_modules/@stdlib/repl/lib/tokenizer.js @@ -32,6 +32,12 @@ var commands = require( './commands.js' ); // VARIABLES // var COMMANDS = commands(); +var isControlKeyword = contains.factory( [ 'if', 'else', 'switch', 'case', 'catch', 'finally', 'try', 'return', 'break', 'continue' ] ); +var isSpecialIdentifier = contains.factory( [ 'this', 'super' ] ); +var isReservedLiteral = contains.factory( [ 'null', 'true', 'false' ] ); +var isUnrecognizedKeyword = contains.factory( [ 'async', 'await', 'let' ] ); +var isStringType = contains.factory( [ 'string', 'template' ] ); +var isLiteralIdentifier = contains.factory( [ 'string', 'boolean', 'number' ] ); var RE_PUNCTUATION = /(\{|\}|\[|\]|\(|\)|,|;|:|\.|\?|\?\.|=>|\.\.\.|`|\${)/; @@ -98,31 +104,31 @@ function tokenizer( line, context ) { if ( token.start === token.end ) { return; } - if ( token.type.isLoop || contains( [ 'if', 'else', 'switch', 'case', 'catch', 'finally', 'try', 'return', 'break', 'continue' ], token.type.keyword ) ) { + if ( token.type.isLoop || isControlKeyword( token.type.keyword ) ) { // Control flow keywords - `for`, `while`, `do`, `if`, `else` etc: token.type = 'control'; tokens.push( token ); return; } - if ( contains( [ 'this', 'super' ], token.type.keyword ) ) { + if ( isSpecialIdentifier( token.type.keyword ) ) { // Special identifiers - `this`, `super`: token.type = 'specialIdentifier'; tokens.push( token ); return; } - if ( contains( [ 'null', 'true', 'false' ], token.type.keyword ) || ( token.type.label === 'name' && token.value === 'undefined' ) ) { + if ( isReservedLiteral( token.type.keyword ) || ( token.type.label === 'name' && token.value === 'undefined' ) ) { // Built-in literals - `true`, `false`, `null`, `undefined`: token.type = 'literal'; tokens.push( token ); return; } - if ( token.type.keyword || ( token.type.label === 'name' && contains( [ 'async', 'await', 'let' ], token.value ) ) ) { + if ( token.type.keyword || ( token.type.label === 'name' && isUnrecognizedKeyword( token.value ) ) ) { // Keywords - `function`, `import`, `var`, `const`, `let` etc.: token.type = 'keyword'; tokens.push( token ); return; } - if ( token.type.label === 'string' || token.type.label === 'template' ) { + if ( isStringType( token.type.label ) ) { // Strings and template string literals: token.type = 'string'; tokens.push( token ); @@ -298,7 +304,7 @@ function tokenizer( line, context ) { break; } // Push token if property exists in context: - if ( contains( [ 'boolean', 'string', 'number' ], typeof obj ) ) { + if ( isLiteralIdentifier( typeof obj ) ) { tokens.push({ 'value': property.value.name, 'type': 'variable', From bbd1e11cbe9c40a8b18d8d61f5866823cc3d1c7b Mon Sep 17 00:00:00 2001 From: Snehil Shah Date: Mon, 27 May 2024 14:20:03 +0000 Subject: [PATCH 15/23] fix: variable names and using `contains.factory` Signed-off-by: Snehil Shah --- lib/node_modules/@stdlib/repl/lib/tokenizer.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/node_modules/@stdlib/repl/lib/tokenizer.js b/lib/node_modules/@stdlib/repl/lib/tokenizer.js index d8f2b5e65606..b9dab1ac029a 100644 --- a/lib/node_modules/@stdlib/repl/lib/tokenizer.js +++ b/lib/node_modules/@stdlib/repl/lib/tokenizer.js @@ -36,8 +36,8 @@ var isControlKeyword = contains.factory( [ 'if', 'else', 'switch', 'case', 'catc var isSpecialIdentifier = contains.factory( [ 'this', 'super' ] ); var isReservedLiteral = contains.factory( [ 'null', 'true', 'false' ] ); var isUnrecognizedKeyword = contains.factory( [ 'async', 'await', 'let' ] ); -var isStringType = contains.factory( [ 'string', 'template' ] ); -var isLiteralIdentifier = contains.factory( [ 'string', 'boolean', 'number' ] ); +var isStringTokenType = contains.factory( [ 'string', 'template' ] ); +var isLiteralType = contains.factory( [ 'string', 'boolean', 'number' ] ); var RE_PUNCTUATION = /(\{|\}|\[|\]|\(|\)|,|;|:|\.|\?|\?\.|=>|\.\.\.|`|\${)/; @@ -128,7 +128,7 @@ function tokenizer( line, context ) { tokens.push( token ); return; } - if ( isStringType( token.type.label ) ) { + if ( isStringTokenType( token.type.label ) ) { // Strings and template string literals: token.type = 'string'; tokens.push( token ); @@ -222,7 +222,7 @@ function tokenizer( line, context ) { // If identifier is in global context, push it as a token... identifier = context[ node.name ]; if ( identifier ) { - if ( contains( [ 'boolean', 'string', 'number' ], typeof identifier ) ) { + if ( isLiteralType( typeof identifier ) ) { tokens.push( { 'value': node.name, 'type': 'variable', @@ -304,7 +304,7 @@ function tokenizer( line, context ) { break; } // Push token if property exists in context: - if ( isLiteralIdentifier( typeof obj ) ) { + if ( isLiteralType( typeof obj ) ) { tokens.push({ 'value': property.value.name, 'type': 'variable', From 7a94ce4744912558a1a5a8715fd37735874afa28 Mon Sep 17 00:00:00 2001 From: Snehil Shah Date: Mon, 27 May 2024 18:23:50 +0000 Subject: [PATCH 16/23] refactor: return all local nodes after resolving them Signed-off-by: Snehil Shah --- .../@stdlib/repl/lib/complete_expression.js | 2 +- .../@stdlib/repl/lib/resolve_globals.js | 2 +- .../@stdlib/repl/lib/resolve_local_scopes.js | 34 ++++++++----------- .../@stdlib/repl/lib/tokenizer.js | 3 +- 4 files changed, 17 insertions(+), 24 deletions(-) diff --git a/lib/node_modules/@stdlib/repl/lib/complete_expression.js b/lib/node_modules/@stdlib/repl/lib/complete_expression.js index 64b664344fad..769d7dd17bf0 100644 --- a/lib/node_modules/@stdlib/repl/lib/complete_expression.js +++ b/lib/node_modules/@stdlib/repl/lib/complete_expression.js @@ -74,7 +74,7 @@ function complete( out, context, expression ) { ast = parse( expression, AOPTS ); debug( 'Resolving local scopes within the AST.' ); - ast = resolveLocalScopes( ast ); + resolveLocalScopes( ast ); // Get the last program top-level AST "node": debug( 'Number of statements: %d', ast.body.length ); diff --git a/lib/node_modules/@stdlib/repl/lib/resolve_globals.js b/lib/node_modules/@stdlib/repl/lib/resolve_globals.js index aaab1d774db2..b97f8419f3f8 100644 --- a/lib/node_modules/@stdlib/repl/lib/resolve_globals.js +++ b/lib/node_modules/@stdlib/repl/lib/resolve_globals.js @@ -76,7 +76,7 @@ function resolveGlobals( ast ) { globals = []; // Resolve local scopes: - ast = resolveLocalScopes( ast ); + resolveLocalScopes( ast ); // Define callbacks for relevant AST nodes: visitors = { diff --git a/lib/node_modules/@stdlib/repl/lib/resolve_local_scopes.js b/lib/node_modules/@stdlib/repl/lib/resolve_local_scopes.js index 244125bae142..a183d90525ab 100644 --- a/lib/node_modules/@stdlib/repl/lib/resolve_local_scopes.js +++ b/lib/node_modules/@stdlib/repl/lib/resolve_local_scopes.js @@ -34,24 +34,21 @@ var isBlockScope = require( './is_block_scope.js' ); * * ## Notes * -* - This function modifies the provided AST by adding a `locals` property to select AST nodes. +* - This function modifies the provided AST by adding a `locals` property to select AST nodes and returns all resolved `locals`. * * @private * @param {AST} ast - abstract syntax tree (AST) -* @param {Array} declarations - array with all declared `Identifier` nodes appended * @throws {TypeError} must provide a program AST node -* @returns {AST} input AST +* @returns {Array} array of nodes of all resolved `locals` in the AST */ -function resolveScopes( ast, declarations ) { - var identifierNodes; +function resolveScopes( ast ) { + var declarations; var VISITORS; - var i; if ( ast.type !== 'Program' ) { throw new TypeError( 'invalid argument. Must provide a program AST node.' ); } - ast.locals = []; - identifierNodes = []; + VISITORS = { 'VariableDeclaration': VariableDeclaration, 'FunctionDeclaration': FunctionDeclaration, @@ -62,13 +59,10 @@ function resolveScopes( ast, declarations ) { 'ImportSpecifier': ModuleImportSpecifier, 'ImportNamespaceSpecifier': ModuleImportSpecifier }; + declarations = []; + ast.locals = []; walk( ast, VISITORS ); - if ( typeof out !== 'undefined' ) { - for ( i = 0; i < identifierNodes.length; i++ ) { - declarations.push( identifierNodes[ i ] ); - } - } - return ast; + return declarations; /** * Resolves identifiers arising from a function declaration, such as function parameters and the function name. @@ -85,7 +79,7 @@ function resolveScopes( ast, declarations ) { } if ( node.id ) { appendUnique( node.locals, node.id.name ); - appendUnique( identifierNodes, node.id ); + appendUnique( declarations, node.id ); } } @@ -105,7 +99,7 @@ function resolveScopes( ast, declarations ) { // Actual identifier name: case 'Identifier': appendUnique( parent.locals, node.name ); - appendUnique( identifierNodes, node ); + appendUnique( declarations, node ); break; // `var { a, b } = { 'a': 10, 'b': 20 }` || `var { ...o } = {}` @@ -193,7 +187,7 @@ function resolveScopes( ast, declarations ) { parent.locals = parent.locals || []; if ( node.id ) { appendUnique( parent.locals, node.id.name ); - appendUnique( identifierNodes, node.id ); + appendUnique( declarations, node.id ); } declareFunction( node ); } @@ -217,7 +211,7 @@ function resolveScopes( ast, declarations ) { parent.locals = parent.locals || []; if ( node.id ) { appendUnique( parent.locals, node.id.name ); - appendUnique( identifierNodes, node.id ); + appendUnique( declarations, node.id ); } } @@ -232,7 +226,7 @@ function resolveScopes( ast, declarations ) { if ( node.handler ) { node.handler.locals = node.handler.locals || []; appendUnique( node.handler.locals, node.handler.param.name ); - appendUnique( identifierNodes, node.handler.param ); + appendUnique( declarations, node.handler.param ); } } @@ -246,7 +240,7 @@ function resolveScopes( ast, declarations ) { function ModuleImportSpecifier( node, parents ) { parents[ 0 ].locals = parents[ 0 ].locals || []; appendUnique( parents[ 0 ].locals, node.local.name ); - appendUnique( identifierNodes, node.local ); + appendUnique( declarations, node.local ); } } diff --git a/lib/node_modules/@stdlib/repl/lib/tokenizer.js b/lib/node_modules/@stdlib/repl/lib/tokenizer.js index b9dab1ac029a..33329538fe40 100644 --- a/lib/node_modules/@stdlib/repl/lib/tokenizer.js +++ b/lib/node_modules/@stdlib/repl/lib/tokenizer.js @@ -68,8 +68,7 @@ function tokenizer( line, context ) { }); // Resolve variable declarations from the given line as tokens... - declarations = []; - ast = resolveLocalScopes( ast, declarations ); + declarations = resolveLocalScopes( ast ); if ( declarations ) { for ( i = 0; i < declarations.length; i++ ) { if ( declarations[ i ].start === declarations[ i ].end ) { From c7cd5325650fce07c6a170838a596a9ce355b94d Mon Sep 17 00:00:00 2001 From: Snehil Shah Date: Mon, 27 May 2024 18:44:11 +0000 Subject: [PATCH 17/23] docs: update jsdoc Signed-off-by: Snehil Shah --- lib/node_modules/@stdlib/repl/lib/resolve_local_scopes.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/node_modules/@stdlib/repl/lib/resolve_local_scopes.js b/lib/node_modules/@stdlib/repl/lib/resolve_local_scopes.js index a183d90525ab..7ec3cdaff12d 100644 --- a/lib/node_modules/@stdlib/repl/lib/resolve_local_scopes.js +++ b/lib/node_modules/@stdlib/repl/lib/resolve_local_scopes.js @@ -39,7 +39,7 @@ var isBlockScope = require( './is_block_scope.js' ); * @private * @param {AST} ast - abstract syntax tree (AST) * @throws {TypeError} must provide a program AST node -* @returns {Array} array of nodes of all resolved `locals` in the AST +* @returns {Array} array of `Identifier` nodes of all resolved `locals` in the AST */ function resolveScopes( ast ) { var declarations; From 186bbacb547b0cf7434f1edd89f7de085e9bcdd4 Mon Sep 17 00:00:00 2001 From: Snehil Shah Date: Tue, 28 May 2024 05:37:54 +0000 Subject: [PATCH 18/23] fix: handle reserved names Signed-off-by: Snehil Shah --- .../@stdlib/repl/lib/tokenizer.js | 56 ++++++++++++------- 1 file changed, 37 insertions(+), 19 deletions(-) diff --git a/lib/node_modules/@stdlib/repl/lib/tokenizer.js b/lib/node_modules/@stdlib/repl/lib/tokenizer.js index 33329538fe40..e96c9c1297c0 100644 --- a/lib/node_modules/@stdlib/repl/lib/tokenizer.js +++ b/lib/node_modules/@stdlib/repl/lib/tokenizer.js @@ -16,6 +16,8 @@ * limitations under the License. */ +/* eslint-disable max-lines-per-function */ + 'use strict'; // MODULES // @@ -38,6 +40,7 @@ var isReservedLiteral = contains.factory( [ 'null', 'true', 'false' ] ); var isUnrecognizedKeyword = contains.factory( [ 'async', 'await', 'let' ] ); var isStringTokenType = contains.factory( [ 'string', 'template' ] ); var isLiteralType = contains.factory( [ 'string', 'boolean', 'number' ] ); +var isReservedName = contains.factory( [ 'undefined', 'async', 'await', 'let' ] ); var RE_PUNCTUATION = /(\{|\}|\[|\]|\(|\)|,|;|:|\.|\?|\?\.|=>|\.\.\.|`|\${)/; @@ -71,9 +74,14 @@ function tokenizer( line, context ) { declarations = resolveLocalScopes( ast ); if ( declarations ) { for ( i = 0; i < declarations.length; i++ ) { + // Ignore non-existent nodes: if ( declarations[ i ].start === declarations[ i ].end ) { continue; } + // Ignore if node is a reserved name: + if ( isReservedName( declarations[ i ].name ) ) { + continue; + } tokens.push({ 'type': 'name', 'value': declarations[ i ].name, @@ -195,6 +203,10 @@ function tokenizer( line, context ) { if ( node.start === node.end ) { return; } + // Ignore if node is a reserved name: + if ( isReservedName( node.name ) ) { + return; + } // If identifier is defined in the local scope, assume and treat it like a `variable` and push it as a token... if ( contains( resolveLocalScope( ast, node ), node.name ) ) { tokens.push({ @@ -256,29 +268,16 @@ function tokenizer( line, context ) { return; } - /** - * Recursively traverses the `MemberExpression` node to populate the `properties` array. - * - * @private - * @param {Object} node - node to be traversed - */ - function traverse( node ) { - if ( node.object.type === 'Identifier' ) { - // Reached a resolvable MemberExpression (a.b in a.b.c.d), save property(b) & object(a) and exit: - properties.unshift( node.property ); - properties.unshift( node.object ); - } else if ( node.object.type === 'MemberExpression' ) { - // Found node with another MemberExpression as object, save property and traverse it: - properties.unshift( node.property ); - traverse( node.object ); - } - } - traverse( node ); + // Resolve members of the `MemberExpression`: + resolveMembers( node ); // Check if the object identifier is valid... property = properties.first(); if ( !property ) { - // Not an object: + return; + } + // Ignore if the object identifier is a reserved name: + if ( isReservedName( property.value.name ) ) { return; } // If object identifier exists in the local scope, don't resolve from global scope... @@ -286,6 +285,7 @@ function tokenizer( line, context ) { if ( contains( locals, property.value.name ) ) { return; } + // Enter object's namespace: obj = context; properties = properties.iterator(); @@ -320,6 +320,24 @@ function tokenizer( line, context ) { } property = properties.next(); } + + /** + * Resolves members of the `MemberExpression` node. + * + * @private + * @param {Object} node - node to be traversed + */ + function resolveMembers( node ) { + if ( node.object.type === 'Identifier' ) { + // Reached a resolvable MemberExpression (a.b in a.b.c.d), save property(b) & object(a) and exit: + properties.unshift( node.property ); + properties.unshift( node.object ); + } else if ( node.object.type === 'MemberExpression' ) { + // Found node with another MemberExpression as object, save property and traverse it: + properties.unshift( node.property ); + resolveMembers( node.object ); + } + } } } From 76afa3c86956f77f0c899f7fd3d6d0ab3b2c4e60 Mon Sep 17 00:00:00 2001 From: Snehil Shah Date: Tue, 28 May 2024 05:42:30 +0000 Subject: [PATCH 19/23] fix: suggestions Signed-off-by: Snehil Shah --- lib/node_modules/@stdlib/repl/lib/syntax_highlighter.js | 2 +- lib/node_modules/@stdlib/repl/lib/tokenizer.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/node_modules/@stdlib/repl/lib/syntax_highlighter.js b/lib/node_modules/@stdlib/repl/lib/syntax_highlighter.js index ad3dc40ba4b3..4cba7313d554 100644 --- a/lib/node_modules/@stdlib/repl/lib/syntax_highlighter.js +++ b/lib/node_modules/@stdlib/repl/lib/syntax_highlighter.js @@ -151,7 +151,7 @@ setNonEnumerableReadOnly( SyntaxHighlighter.prototype, 'onKeypress', function on } // Highlight: - debug( '%s tokens found. Highlighting...', tokens.length ); + debug( '%d tokens found. Highlighting...', tokens.length ); highlightedLine = this._highlightLine( this._line, tokens ); // Replace: diff --git a/lib/node_modules/@stdlib/repl/lib/tokenizer.js b/lib/node_modules/@stdlib/repl/lib/tokenizer.js index e96c9c1297c0..c15e83a45e41 100644 --- a/lib/node_modules/@stdlib/repl/lib/tokenizer.js +++ b/lib/node_modules/@stdlib/repl/lib/tokenizer.js @@ -171,7 +171,7 @@ function tokenizer( line, context ) { * Callback invoked upon encountering a `Comment` when parsing. * * @private - * @param {Object} block - true if the comment is a block comment, false if it is a line comment + * @param {boolean} block - true if the comment is a block comment, false if it is a line comment * @param {string} text - comment value * @param {number} start - start index * @param {number} end - end index From 34f53d06c1e6e42d48818c044c7437f57adfa419 Mon Sep 17 00:00:00 2001 From: Snehil Shah Date: Tue, 28 May 2024 06:01:31 +0000 Subject: [PATCH 20/23] docs: clarify comments Signed-off-by: Snehil Shah --- .../@stdlib/repl/lib/tokenizer.js | 38 +++++++++---------- 1 file changed, 18 insertions(+), 20 deletions(-) diff --git a/lib/node_modules/@stdlib/repl/lib/tokenizer.js b/lib/node_modules/@stdlib/repl/lib/tokenizer.js index c15e83a45e41..ee06c11edeb8 100644 --- a/lib/node_modules/@stdlib/repl/lib/tokenizer.js +++ b/lib/node_modules/@stdlib/repl/lib/tokenizer.js @@ -72,23 +72,21 @@ function tokenizer( line, context ) { // Resolve variable declarations from the given line as tokens... declarations = resolveLocalScopes( ast ); - if ( declarations ) { - for ( i = 0; i < declarations.length; i++ ) { - // Ignore non-existent nodes: - if ( declarations[ i ].start === declarations[ i ].end ) { - continue; - } - // Ignore if node is a reserved name: - if ( isReservedName( declarations[ i ].name ) ) { - continue; - } - tokens.push({ - 'type': 'name', - 'value': declarations[ i ].name, - 'start': declarations[ i ].start, - 'end': declarations[ i ].end - }); + for ( i = 0; i < declarations.length; i++ ) { + // Ignore placeholder nodes: + if ( declarations[ i ].start === declarations[ i ].end ) { + continue; } + // Ignore if node is a reserved name: + if ( isReservedName( declarations[ i ].name ) ) { + continue; + } + tokens.push({ + 'type': 'name', + 'value': declarations[ i ].name, + 'start': declarations[ i ].start, + 'end': declarations[ i ].end + }); } // Resolve identifiers from the given line as tokens... @@ -107,7 +105,7 @@ function tokenizer( line, context ) { * @param {Object} token - token object */ function onToken( token ) { - // Ignore non-existent tokens: + // Ignore placeholders & EOF tokens: if ( token.start === token.end ) { return; } @@ -171,7 +169,7 @@ function tokenizer( line, context ) { * Callback invoked upon encountering a `Comment` when parsing. * * @private - * @param {boolean} block - true if the comment is a block comment, false if it is a line comment + * @param {boolean} block - boolean indicating whether a comment is a block comment * @param {string} text - comment value * @param {number} start - start index * @param {number} end - end index @@ -199,7 +197,7 @@ function tokenizer( line, context ) { var command; var i; - // Ignore non-existent nodes: + // Ignore placeholder nodes: if ( node.start === node.end ) { return; } @@ -263,7 +261,7 @@ function tokenizer( line, context ) { var locals; var obj; - // Ignore non-existent nodes: + // Ignore placeholder nodes: if ( node.start === node.end ) { return; } From 94c2e01deb7cf6971ec3cc4b66be74fd117c3360 Mon Sep 17 00:00:00 2001 From: Snehil Shah Date: Tue, 28 May 2024 07:50:43 +0000 Subject: [PATCH 21/23] fix: avoid conflicts between reserved names and object properties Signed-off-by: Snehil Shah --- .../@stdlib/repl/lib/tokenizer.js | 46 ++++++++++++++++--- 1 file changed, 39 insertions(+), 7 deletions(-) diff --git a/lib/node_modules/@stdlib/repl/lib/tokenizer.js b/lib/node_modules/@stdlib/repl/lib/tokenizer.js index ee06c11edeb8..883e815b355c 100644 --- a/lib/node_modules/@stdlib/repl/lib/tokenizer.js +++ b/lib/node_modules/@stdlib/repl/lib/tokenizer.js @@ -77,13 +77,29 @@ function tokenizer( line, context ) { if ( declarations[ i ].start === declarations[ i ].end ) { continue; } - // Ignore if node is a reserved name: - if ( isReservedName( declarations[ i ].name ) ) { + // If node is an unrecognized `literal`, push it as a token: + if ( declarations[ i ].name === 'undefined' ) { + tokens.push({ + 'value': declarations[ i ].name, + 'type': 'literal', + 'start': declarations[ i ].start, + 'end': declarations[ i ].end + }); + continue; + } + // If node is an unrecognized `keyword`, push it as a token: + if ( isUnrecognizedKeyword( declarations[ i ].name ) ) { + tokens.push({ + 'value': declarations[ i ].name, + 'type': 'keyword', + 'start': declarations[ i ].start, + 'end': declarations[ i ].end + }); continue; } tokens.push({ - 'type': 'name', 'value': declarations[ i ].name, + 'type': 'name', 'start': declarations[ i ].start, 'end': declarations[ i ].end }); @@ -121,13 +137,13 @@ function tokenizer( line, context ) { tokens.push( token ); return; } - if ( isReservedLiteral( token.type.keyword ) || ( token.type.label === 'name' && token.value === 'undefined' ) ) { + if ( isReservedLiteral( token.type.keyword ) ) { // Built-in literals - `true`, `false`, `null`, `undefined`: token.type = 'literal'; tokens.push( token ); return; } - if ( token.type.keyword || ( token.type.label === 'name' && isUnrecognizedKeyword( token.value ) ) ) { + if ( token.type.keyword ) { // Keywords - `function`, `import`, `var`, `const`, `let` etc.: token.type = 'keyword'; tokens.push( token ); @@ -201,8 +217,24 @@ function tokenizer( line, context ) { if ( node.start === node.end ) { return; } - // Ignore if node is a reserved name: - if ( isReservedName( node.name ) ) { + // If node is an unrecognized `literal`, push it as a token: + if ( node.name === 'undefined' ) { + tokens.push({ + 'value': node.name, + 'type': 'literal', + 'start': node.start, + 'end': node.end + }); + return; + } + // If node is an unrecognized `keyword`, push it as a token: + if ( isUnrecognizedKeyword( node.name ) ) { + tokens.push({ + 'value': node.name, + 'type': 'keyword', + 'start': node.start, + 'end': node.end + }); return; } // If identifier is defined in the local scope, assume and treat it like a `variable` and push it as a token... From b2fd37924f2b9c2234566663d1401705bfebe5f0 Mon Sep 17 00:00:00 2001 From: Snehil Shah Date: Tue, 28 May 2024 10:34:12 +0000 Subject: [PATCH 22/23] fix: detect if identifier exists before pushing a `name` token Signed-off-by: Snehil Shah --- .../@stdlib/repl/lib/tokenizer.js | 52 ++++++------------- 1 file changed, 17 insertions(+), 35 deletions(-) diff --git a/lib/node_modules/@stdlib/repl/lib/tokenizer.js b/lib/node_modules/@stdlib/repl/lib/tokenizer.js index 883e815b355c..174034beb553 100644 --- a/lib/node_modules/@stdlib/repl/lib/tokenizer.js +++ b/lib/node_modules/@stdlib/repl/lib/tokenizer.js @@ -73,42 +73,21 @@ function tokenizer( line, context ) { // Resolve variable declarations from the given line as tokens... declarations = resolveLocalScopes( ast ); for ( i = 0; i < declarations.length; i++ ) { - // Ignore placeholder nodes: - if ( declarations[ i ].start === declarations[ i ].end ) { - continue; - } - // If node is an unrecognized `literal`, push it as a token: - if ( declarations[ i ].name === 'undefined' ) { - tokens.push({ - 'value': declarations[ i ].name, - 'type': 'literal', - 'start': declarations[ i ].start, - 'end': declarations[ i ].end - }); - continue; - } - // If node is an unrecognized `keyword`, push it as a token: - if ( isUnrecognizedKeyword( declarations[ i ].name ) ) { + // If declaration cannot be resolved as an identifier, push it as a `name` token: + if ( !resolveIdentifier( declarations[ i ] ) ) { tokens.push({ 'value': declarations[ i ].name, - 'type': 'keyword', + 'type': 'name', 'start': declarations[ i ].start, 'end': declarations[ i ].end }); - continue; } - tokens.push({ - 'value': declarations[ i ].name, - 'type': 'name', - 'start': declarations[ i ].start, - 'end': declarations[ i ].end - }); } // Resolve identifiers from the given line as tokens... VISITORS = { - 'Identifier': Identifier, - 'MemberExpression': MemberExpression + 'Identifier': resolveIdentifier, + 'MemberExpression': resolveMemberExpression }; walk.simple( ast, VISITORS ); @@ -203,19 +182,20 @@ function tokenizer( line, context ) { } /** - * Callback invoked upon encountering a `Identifier` AST node. + * Resolves an `Identifier` node. * * @private * @param {Object} node - AST node + * @returns {boolean} boolean indicating whether the `Identifier` was resolved */ - function Identifier( node ) { + function resolveIdentifier( node ) { var identifier; var command; var i; // Ignore placeholder nodes: if ( node.start === node.end ) { - return; + return true; } // If node is an unrecognized `literal`, push it as a token: if ( node.name === 'undefined' ) { @@ -225,7 +205,7 @@ function tokenizer( line, context ) { 'start': node.start, 'end': node.end }); - return; + return true; } // If node is an unrecognized `keyword`, push it as a token: if ( isUnrecognizedKeyword( node.name ) ) { @@ -235,7 +215,7 @@ function tokenizer( line, context ) { 'start': node.start, 'end': node.end }); - return; + return true; } // If identifier is defined in the local scope, assume and treat it like a `variable` and push it as a token... if ( contains( resolveLocalScope( ast, node ), node.name ) ) { @@ -245,7 +225,7 @@ function tokenizer( line, context ) { 'start': node.start, 'end': node.end }); - return; + return true; } // If identifier is a REPL command, push it as a token... for ( i = 0; i < COMMANDS.length; i++ ) { @@ -257,7 +237,7 @@ function tokenizer( line, context ) { 'start': node.start, 'end': node.end }); - return; + return true; } } // If identifier is in global context, push it as a token... @@ -278,16 +258,18 @@ function tokenizer( line, context ) { 'end': node.end }); } + return true; } + return false; } /** - * Callback invoked upon encountering a `MemberExpression` AST node. + * Resolves a `MemberExpression` node. * * @private * @param {Object} node - AST node */ - function MemberExpression( node ) { + function resolveMemberExpression( node ) { var properties = linkedList(); var property; var locals; From 9e5015d2e25f359482ef7b92ece47807d83dec66 Mon Sep 17 00:00:00 2001 From: Snehil Shah Date: Fri, 31 May 2024 05:02:34 +0000 Subject: [PATCH 23/23] style: fix bracket spacing Signed-off-by: Snehil Shah --- lib/node_modules/@stdlib/repl/lib/tokenizer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/node_modules/@stdlib/repl/lib/tokenizer.js b/lib/node_modules/@stdlib/repl/lib/tokenizer.js index 174034beb553..65c9fb85212b 100644 --- a/lib/node_modules/@stdlib/repl/lib/tokenizer.js +++ b/lib/node_modules/@stdlib/repl/lib/tokenizer.js @@ -25,7 +25,7 @@ var parse = require( 'acorn-loose' ).parse; var walk = require( 'acorn-walk' ); var linkedList = require( '@stdlib/utils/linked-list' ); -var contains = require('@stdlib/array/base/assert/contains'); +var contains = require( '@stdlib/array/base/assert/contains' ); var resolveLocalScopes = require( './resolve_local_scopes.js' ); var resolveLocalScope = require( './resolve_local_scope.js' ); var commands = require( './commands.js' );