refactor(scriptcheck): simplify lexer imports and state

This commit is contained in:
Dylan M. Taylor
2026-10-27 17:47:12 +00:00
parent e985394460
commit 8b2439678b
4 changed files with 64 additions and 57 deletions
+1 -1
View File
@@ -27,6 +27,7 @@ use crate::catalog::{self, FeatureId};
use crate::directives;
use crate::lex::{Lexer, T, Token, expr_after};
use crate::report::{self, Acc, OutsideId};
use sig::{COMPOUND, LineBreaks, NEWER, Read, Sig, ends_value, for_in, goes_on_after, goes_on_with, is, member_mark, newer_number, unquote, word};
mod declared;
mod features;
@@ -34,7 +35,6 @@ mod sig;
pub(crate) use declared::{Declared, declarations};
use features::platform_name;
use sig::*;
/// An `#include` line: what it names, its line, and the folders `#includepath` named at that point
/// (one list, shared by every line it applies to).
+3 -1
View File
@@ -1,6 +1,8 @@
//! The names declared, read before the analysis.
use super::*;
use std::collections::HashSet;
use super::{Acc, Brackets, Lexer, LineBreaks, Read, Sig, T, for_in, goes_on_after, goes_on_with, is, member_mark, word};
/// The names a file's function bodies and its global code declare, read before the analysis. A
/// function's `var` statements and the function declarations directly in its body make the names
+1 -1
View File
@@ -1,6 +1,6 @@
//! The platform features, menu commands and later syntax the analysis recognises.
use super::*;
use super::{Analyzer, FeatureId, NEWER, OutsideId, Pending, Sig, T, catalog, is, member_mark, newer_number, report, unquote, word};
/// A global name of the platform the report looks for (`$`, `File`, `Socket`, `alert`, `eval`…).
pub(super) fn platform_name(n: &str) -> bool {
+59 -54
View File
@@ -699,6 +699,64 @@ impl<'s> Lexer<'s> {
}
}
impl<'s> Lexer<'s> {
/// Update expression, statement and nesting state after reading a significant token.
fn update_context(&mut self, kind: T, text: &str) {
self.line_start = false;
// A name after `.`, `..`, `.@` or `::` is a property's, also when it is a keyword: no
// expression or statement starts after it.
let property = kind == T::Ident && self.member;
// After the `)` of a condition comes a statement, which may start with a regular
// expression or an XML literal.
let condition = kind == T::Punct && self.paren(text);
// A statement may also start after the `}` of a block. After the `}` of an object
// literal or of a function expression's body, `/` divides.
let brace = if kind == T::Punct { self.brace(text) } else { None };
self.expr = condition
|| match (text, brace) {
("}", Some(brace)) => brace == Brace::Block,
_ => !property && expr_after(kind, text),
};
if kind == T::Ident && !property && text == "function" {
self.function = Some(!self.stmt);
}
if kind == T::Punct {
match text {
"(" | "[" | "{" => self.open_level(),
")" | "]" | "}" => self.close_level(),
_ => {}
}
}
let ternary = kind == T::Punct && self.conditional(text);
// A statement may start inside a block or a function's body, after `;`, `=>`, a
// preprocessor line, `else`, `do`, `try` and `finally`, and after the `:` of a label or
// a `case` (not a conditional's, and outside an object literal).
self.stmt = condition
|| match (text, brace) {
("{", Some(brace)) => brace != Brace::Object,
("}", Some(brace)) => brace == Brace::Block,
_ => false,
}
|| kind == T::Directive
|| (kind == T::Punct && matches!(text, ";" | "=>"))
|| (kind == T::Punct && text == ":" && !ternary && self.braces.last() != Some(&Brace::Object))
|| (kind == T::Ident && !property && matches!(text, "else" | "do" | "try" | "finally"));
self.cond = kind == T::Ident && !property && (matches!(text, "if" | "while" | "for" | "with") || (text == "each" && self.cond));
self.restricted = kind == T::Ident && !property && matches!(text, "return" | "throw" | "break" | "continue");
self.member = kind == T::Punct && matches!(text, "." | ".." | ".@" | "::");
// The braces inside the code of an XML literal.
if kind == T::Punct
&& let Some(Level::Code { braces }) = self.xml.last_mut()
{
match text {
"{" => *braces = braces.saturating_add(1),
"}" => *braces = braces.saturating_sub(1),
_ => {}
}
}
}
}
impl<'s> Iterator for Lexer<'s> {
type Item = Token<'s>;
@@ -789,60 +847,7 @@ impl<'s> Iterator for Lexer<'s> {
self.line_ended();
}
}
_ => {
self.line_start = false;
// A name after `.`, `..`, `.@` or `::` is a property's, also when it is a keyword: no
// expression or statement starts after it.
let property = kind == T::Ident && self.member;
// After the `)` of a condition comes a statement, which may start with a regular
// expression or an XML literal.
let condition = kind == T::Punct && self.paren(text);
// A statement may also start after the `}` of a block. After the `}` of an object
// literal or of a function expression's body, `/` divides.
let brace = if kind == T::Punct { self.brace(text) } else { None };
self.expr = condition
|| match (text, brace) {
("}", Some(brace)) => brace == Brace::Block,
_ => !property && expr_after(kind, text),
};
if kind == T::Ident && !property && text == "function" {
self.function = Some(!self.stmt);
}
if kind == T::Punct {
match text {
"(" | "[" | "{" => self.open_level(),
")" | "]" | "}" => self.close_level(),
_ => {}
}
}
let ternary = kind == T::Punct && self.conditional(text);
// A statement may start inside a block or a function's body, after `;`, `=>`, a
// preprocessor line, `else`, `do`, `try` and `finally`, and after the `:` of a label or
// a `case` (not a conditional's, and outside an object literal).
self.stmt = condition
|| match (text, brace) {
("{", Some(brace)) => brace != Brace::Object,
("}", Some(brace)) => brace == Brace::Block,
_ => false,
}
|| kind == T::Directive
|| (kind == T::Punct && matches!(text, ";" | "=>"))
|| (kind == T::Punct && text == ":" && !ternary && self.braces.last() != Some(&Brace::Object))
|| (kind == T::Ident && !property && matches!(text, "else" | "do" | "try" | "finally"));
self.cond = kind == T::Ident && !property && (matches!(text, "if" | "while" | "for" | "with") || (text == "each" && self.cond));
self.restricted = kind == T::Ident && !property && matches!(text, "return" | "throw" | "break" | "continue");
self.member = kind == T::Punct && matches!(text, "." | ".." | ".@" | "::");
// The braces inside the code of an XML literal.
if kind == T::Punct
&& let Some(Level::Code { braces }) = self.xml.last_mut()
{
match text {
"{" => *braces = braces.saturating_add(1),
"}" => *braces = braces.saturating_sub(1),
_ => {}
}
}
}
_ => self.update_context(kind, text),
}
Some(Token { kind, text, line, problem, stmt, expr, start })
}