mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-10-29 04:07:12 -06:00
refactor(scriptcheck): simplify lexer imports and state
This commit is contained in:
@@ -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).
|
||||
|
||||
@@ -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,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 {
|
||||
|
||||
@@ -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 })
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user