pub use crate::error::{
InterpolatedStringErrorType, LexicalErrorType, ParseError, ParseErrorType,
UnsupportedSyntaxError, UnsupportedSyntaxErrorKind,
};
pub use crate::parser::ParseOptions;
use crate::parser::Parser;
use ruff_python_ast::token::Tokens;
use ruff_python_ast::{
Expr, Mod, ModExpression, ModModule, PySourceType, StringFlags, StringLiteral, Suite,
};
use ruff_text_size::{Ranged, TextRange};
mod error;
pub mod lexer;
mod parser;
pub mod semantic_errors;
mod string;
mod token;
mod token_set;
mod token_source;
pub mod typing;
pub fn parse_module(source: &str) -> Result<Parsed<ModModule>, ParseError> {
Parser::new(source, ParseOptions::from(Mode::Module))
.parse()
.try_into_module()
.unwrap()
.into_result()
}
pub fn parse_expression(source: &str) -> Result<Parsed<ModExpression>, ParseError> {
Parser::new(source, ParseOptions::from(Mode::Expression))
.parse()
.try_into_expression()
.unwrap()
.into_result()
}
pub fn parse_expression_range(
source: &str,
range: TextRange,
) -> Result<Parsed<ModExpression>, ParseError> {
let source = &source[..range.end().to_usize()];
Parser::new_starts_at(source, range.start(), ParseOptions::from(Mode::Expression))
.parse()
.try_into_expression()
.unwrap()
.into_result()
}
pub fn parse_parenthesized_expression_range(
source: &str,
range: TextRange,
) -> Result<Parsed<ModExpression>, ParseError> {
let source = &source[..range.end().to_usize()];
let parsed = Parser::new_starts_at(
source,
range.start(),
ParseOptions::from(Mode::ParenthesizedExpression),
)
.parse();
parsed.try_into_expression().unwrap().into_result()
}
pub fn parse_string_annotation(
source: &str,
string: &StringLiteral,
) -> Result<Parsed<ModExpression>, ParseError> {
let range = string
.range()
.add_start(string.flags.opener_len())
.sub_end(string.flags.closer_len());
let source = &source[..range.end().to_usize()];
if string.flags.is_triple_quoted() {
parse_parenthesized_expression_range(source, range)
} else {
parse_expression_range(source, range)
}
}
pub fn parse(source: &str, options: ParseOptions) -> Result<Parsed<Mod>, ParseError> {
parse_unchecked(source, options).into_result()
}
pub fn parse_unchecked(source: &str, options: ParseOptions) -> Parsed<Mod> {
Parser::new(source, options).parse()
}
pub fn parse_unchecked_source(source: &str, source_type: PySourceType) -> Parsed<ModModule> {
Parser::new(source, ParseOptions::from(source_type))
.parse()
.try_into_module()
.unwrap()
}
#[derive(Debug, PartialEq, Clone, get_size2::GetSize)]
pub struct Parsed<T> {
syntax: T,
tokens: Tokens,
errors: Vec<ParseError>,
unsupported_syntax_errors: Vec<UnsupportedSyntaxError>,
}
impl<T> Parsed<T> {
pub fn syntax(&self) -> &T {
&self.syntax
}
pub fn tokens(&self) -> &Tokens {
&self.tokens
}
pub fn errors(&self) -> &[ParseError] {
&self.errors
}
pub fn unsupported_syntax_errors(&self) -> &[UnsupportedSyntaxError] {
&self.unsupported_syntax_errors
}
pub fn into_syntax(self) -> T {
self.syntax
}
pub fn into_errors(self) -> Vec<ParseError> {
self.errors
}
pub fn has_valid_syntax(&self) -> bool {
self.errors.is_empty()
}
pub fn has_invalid_syntax(&self) -> bool {
!self.has_valid_syntax()
}
pub fn has_no_syntax_errors(&self) -> bool {
self.has_valid_syntax() && self.unsupported_syntax_errors.is_empty()
}
pub fn has_syntax_errors(&self) -> bool {
!self.has_no_syntax_errors()
}
pub fn as_result(&self) -> Result<&Parsed<T>, &[ParseError]> {
if self.has_valid_syntax() {
Ok(self)
} else {
Err(&self.errors)
}
}
pub(crate) fn into_result(self) -> Result<Parsed<T>, ParseError> {
if self.has_valid_syntax() {
Ok(self)
} else {
Err(self.into_errors().into_iter().next().unwrap())
}
}
}
impl Parsed<Mod> {
pub fn try_into_module(self) -> Option<Parsed<ModModule>> {
match self.syntax {
Mod::Module(module) => Some(Parsed {
syntax: module,
tokens: self.tokens,
errors: self.errors,
unsupported_syntax_errors: self.unsupported_syntax_errors,
}),
Mod::Expression(_) => None,
}
}
pub fn try_into_expression(self) -> Option<Parsed<ModExpression>> {
match self.syntax {
Mod::Module(_) => None,
Mod::Expression(expression) => Some(Parsed {
syntax: expression,
tokens: self.tokens,
errors: self.errors,
unsupported_syntax_errors: self.unsupported_syntax_errors,
}),
}
}
}
impl Parsed<ModModule> {
pub fn suite(&self) -> &Suite {
&self.syntax.body
}
pub fn into_suite(self) -> Suite {
self.syntax.body
}
}
impl Parsed<ModExpression> {
pub fn expr(&self) -> &Expr {
&self.syntax.body
}
pub fn expr_mut(&mut self) -> &mut Expr {
&mut self.syntax.body
}
pub fn into_expr(self) -> Expr {
*self.syntax.body
}
}
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
pub enum Mode {
Module,
Expression,
ParenthesizedExpression,
Ipython,
}
impl std::str::FromStr for Mode {
type Err = ModeParseError;
fn from_str(s: &str) -> Result<Self, ModeParseError> {
match s {
"exec" | "single" => Ok(Mode::Module),
"eval" => Ok(Mode::Expression),
"ipython" => Ok(Mode::Ipython),
_ => Err(ModeParseError),
}
}
}
pub trait AsMode {
fn as_mode(&self) -> Mode;
}
impl AsMode for PySourceType {
fn as_mode(&self) -> Mode {
match self {
PySourceType::Python | PySourceType::Stub => Mode::Module,
PySourceType::Ipynb => Mode::Ipython,
}
}
}
#[derive(Debug)]
pub struct ModeParseError;
impl std::fmt::Display for ModeParseError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, r#"mode must be "exec", "eval", "ipython", or "single""#)
}
}