one parser call: js.parse(entry, offset, { end, stopAt }) replaces the five parse-at methods, the answer is kept whole for its tables, and a scope's bindings are grouped here

goodbye-acorn
Nic 17 hours ago
parent 5d7a91864c
commit 57c7de58a5

@ -13,36 +13,23 @@ import { keep_tables } from '../../utils/ast.js';
* @returns {Program}
*/
export function parse(source, comments, typescript) {
let ast;
let answer;
try {
ast = teasel.parse(source, {
answer = new teasel.Source(source, {
sourceType: 'module',
typescript,
comments: true,
locations: true,
scopes: true
});
}).parse();
} catch (err) {
return handle_parse_error(err);
}
add_comments(source, comments, /** @type {teasel.Comment[]} */ (ast.comments));
delete ast.comments;
keep_tables(ast, tables(ast));
return ast;
}
add_comments(source, comments, /** @type {teasel.Comment[]} */ (answer.comments));
keep_tables(answer.node, answer);
/**
* Takes the tables off an answer, so they reach the scope analysis without showing in the tree.
* @param {{ scopes?: any; bindings?: any; references?: any }} answer
*/
function tables(answer) {
const { scopes, bindings, references } = answer;
delete answer.scopes;
delete answer.bindings;
delete answer.references;
return { scopes, bindings, references };
return answer.node;
}
/**
@ -53,9 +40,9 @@ function tables(answer) {
* @returns {Program}
*/
export function parse_script(parser, start, end) {
let ast;
let answer;
try {
ast = parser.js.parse(start, end);
answer = parser.js.parse('program', start, { end });
} catch (err) {
return handle_parse_error(err);
}
@ -63,15 +50,12 @@ export function parse_script(parser, start, end) {
add_comments(
parser.template,
parser.root.comments,
/** @type {teasel.Comment[]} */ (ast.comments)
/** @type {teasel.Comment[]} */ (answer.comments)
);
delete ast.comments;
delete ast.errors;
keep_tables(ast, tables(ast));
unsupported(ast.typescript);
delete ast.typescript;
keep_tables(answer.node, answer);
unsupported(answer.typescript);
return ast;
return answer.node;
}
/**
@ -109,8 +93,8 @@ function read(parser, run) {
*/
export function read_expression(parser, stop_at) {
const start = parser.index;
const answer = read(parser, (js) => js.parseExpressionAt(start, stop_at));
keep_tables(answer.node, tables(answer));
const answer = read(parser, (js) => js.parse('expression', start, { stopAt: stop_at }));
keep_tables(answer.node, answer);
const node = answer.node;
// the language tools copy an expression's text by its node's range, so the placeholder standing for one that could not be read spans what was read
if (node.type === 'Identifier' && node.name === '' && node.start === node.end) {
@ -146,8 +130,8 @@ export function read_pattern(parser) {
}
}
const answer = read(parser, (js) => js.parsePatternAt(start));
keep_tables(answer.node, tables(answer));
const answer = read(parser, (js) => js.parse('pattern', start));
keep_tables(answer.node, answer);
return answer.node;
}
@ -157,9 +141,9 @@ export function read_pattern(parser) {
*/
export function read_params(parser) {
const start = parser.index;
const answer = read(parser, (js) => js.parseParamsAt(start));
keep_tables(answer.params, tables(answer));
return answer.params;
const answer = read(parser, (js) => js.parse('params', start));
keep_tables(answer.node, answer);
return answer.node;
}
/**
@ -167,7 +151,7 @@ export function read_params(parser) {
*/
export function read_type_parameters(parser) {
const start = parser.index;
read(parser, (js) => js.parseTypeParametersAt(start));
read(parser, (js) => js.parse('typeParameters', start));
}
/**
@ -177,8 +161,8 @@ export function read_type_parameters(parser) {
export function read_statement(parser) {
const start = parser.index;
const answer = read(parser, (js) => js.parseStatementAt(start));
keep_tables(answer.node, tables(answer));
const answer = read(parser, (js) => js.parse('statement', start));
keep_tables(answer.node, answer);
return answer.node;
}

@ -174,7 +174,7 @@ function open(parser) {
if (parser.eat('(')) {
parser.allow_whitespace();
key = read_expression(parser, undefined, '(');
key = read_expression(parser);
parser.allow_whitespace();
parser.eat(')', true);
parser.allow_whitespace();

@ -1004,14 +1004,14 @@ export function create_scopes(ast, root, allow_reactive_declarations, parent) {
const from_parser = new Map();
/**
* Declares in `scope` what the parser found declared in `parsed`: its parameters, or the rest.
* Declares in `scope` what the parser found declared in one of its scopes: the parameters, or the rest.
* @param {Scope} scope
* @param {import('@teasel/parser').Scope} parsed
* @param {import('@teasel/parser').Binding[]} bindings
* @param {boolean} params
* @param {boolean} [template] the declarations are a const tag's
*/
function declare_parsed(scope, parsed, params, template = false) {
for (const binding of parsed.bindings) {
function declare_parsed(scope, bindings, params, template = false) {
for (const binding of bindings) {
if ((binding.kind === 'param') !== params) continue;
// no node: `arguments`, or a declaration erased with the TypeScript it belonged to
if (binding.node === null || binding.kind === 'class-name' || binding.kind === 'pattern')
@ -1086,6 +1086,15 @@ export function create_scopes(ast, root, allow_reactive_declarations, parent) {
}
return;
}
/** @type {Map<import('@teasel/parser').Scope, import('@teasel/parser').Binding[]>} the answer's bindings by the scope that holds them */
const by_scope = new Map();
for (const binding of answer.bindings) {
const held = by_scope.get(binding.scope);
if (held === undefined) by_scope.set(binding.scope, [binding]);
else held.push(binding);
}
/** @param {import('@teasel/parser').Scope} parsed */
const of = (parsed) => by_scope.get(parsed) ?? [];
/** @type {Map<import('@teasel/parser').Scope, Scope>} the parser's scopes and ours; a function's holds its parameters */
const ours = new Map([[outermost, scope]]);
/** @type {Map<import('@teasel/parser').Scope, Scope>} a function's body, which holds the rest */
@ -1108,7 +1117,7 @@ export function create_scopes(ast, root, allow_reactive_declarations, parent) {
return /** @type {Scope} */ (ours.get(scope));
}
declare_parsed(scope, outermost, false, template);
declare_parsed(scope, of(outermost), false, template);
for (let i = 1; i < answer.scopes.length; i++) {
const parsed = answer.scopes[i];
@ -1124,18 +1133,18 @@ export function create_scopes(ast, root, allow_reactive_declarations, parent) {
scopes.set(node, params);
ours.set(parsed, params);
if (parsed.parent?.kind === 'function-name') ours.set(parsed.parent, params);
declare_parsed(params, parsed, true);
declare_parsed(params, of(parsed), true);
if (
node.body.type !== 'BlockStatement' ||
(node.type === 'FunctionExpression' && node.id)
) {
declare_parsed(params, parsed, false);
declare_parsed(params, of(parsed), false);
}
if (node.body.type === 'BlockStatement') {
const body = params.child();
scopes.set(node.body, body);
bodies.set(parsed, body);
declare_parsed(body, parsed, false);
declare_parsed(body, of(parsed), false);
}
break;
}
@ -1147,7 +1156,7 @@ export function create_scopes(ast, root, allow_reactive_declarations, parent) {
const inside = parent.child(true);
scopes.set(node, inside);
ours.set(parsed, inside);
declare_parsed(inside, parsed, false);
declare_parsed(inside, of(parsed), false);
break;
}
default:

@ -4,21 +4,21 @@ import { walk } from 'zimmerframe';
import { bindingOf } from '@teasel/parser';
/**
* @typedef {{ scopes: import('@teasel/parser').Scope[]; bindings: import('@teasel/parser').Binding[]; references: import('@teasel/parser').Reference[] }} Tables
* the parser's scope, binding and reference tables for one answer
* @typedef {Required<Pick<import('@teasel/parser').Parsed<unknown>, 'scopes' | 'bindings' | 'references'>>} Tables
* the parser's scope, binding and reference tables for one answer, parsed with `scopes`
*/
// a symbol on the root: a property load where the walk asks every node, not a map lookup
const PARSED = Symbol('parsed');
/**
* Keeps the parser's tables on the root it parsed, a script's program or a template's
* expression, pattern, parameter list or declaration.
* Keeps the parser's answer on the root it parsed, a script's program or a template's
* expression, pattern, parameter list or declaration, for its tables.
* @param {object} root
* @param {Tables} tables
* @param {import('@teasel/parser').Parsed<unknown>} answer
*/
export function keep_tables(root, tables) {
/** @type {any} */ (root)[PARSED] = tables;
export function keep_tables(root, answer) {
/** @type {any} */ (root)[PARSED] = answer;
}
/**

Loading…
Cancel
Save